Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Pesni_o_Paskale_2012-11-23.pdf
Скачиваний:
35
Добавлен:
19.03.2016
Размер:
5.16 Mб
Скачать

Глава 31 Финал журнальной истории

В предыдущей главе мы поклялись восстановить съеденную мышами программу и отчасти сдержали клятву. Нами решена упрощенная задача — обработка журнала без фамилий учеников, то есть, мы исполнили вычислительную часть проекта. Теперь завершим его, добившись обработки настоящего классного журнала. Требуется, казалось бы, пустяк — прочесть фамилии учеников. Но воспользоваться процедурой Readln, как мы поступили в программе шифрования текста, здесь не получится, — она прочитает всю строку целиком, включая и оценки (которые станут как бы частью фамилии!).

Буква за буквой

Славный литературный герой Остап Бендер по поводу желанного миллиона сказал так: «Я бы взял частями, но мне нужно сразу!». Увы! При чтении фамилий надо проявить терпение. Если не получается сразу, возьмем по частям. Ведь строка фамилии состоит из отдельных букв, — так прочитаем фамилию по буквам! Прочитать букву может все та же процедура Read, например:

var sym : char;

. . .

Read(InFile, sym);

{ чтение одного символа }

А фамилию S склеим из отдельных букв:

S:= S + sym;

Разумеется, что здесь нужен цикл, условием выхода из которого будет либо достижение первого пробела, либо достижение конца строки. В этом и состоит основная идея алгоритма, показанного на рис. 71.

210

Глава 31

Финал журнальной истории

Очистить накопитель строки

Прочитать первый символ

Пробел или конец строки?

while

Да

Нет

Добавить символ к накопителю

Прочитать очередной символ

Рис. 71 – Упрощенный алгоритм побуквенного чтения фамилии

Нелишняя предосторожность

Людям свойственно ошибаться, — даже учителям! В строках журнала (а это текстовый файл) могут оказаться лишние пробелы — как между оценками, так и в начале строки, перед фамилией. И что тогда? — проверьте на практике. При чтении чисел процедура Read «не заметит» лишних пробелов, — она достаточно «умна». Другое дело — показанная выше блок-схема: если перед фамилией обнаружится пробел, то чтение слова завершится досрочно. Стало быть, для правильного чтения фамилии надо пропустить стоящие перед нею пробелы (если они есть). Это улучшение слегка усложнит блок-схему (рис. 72).

211

Глава 31

Финал журнальной истории

Очистить накопитель строки

Repeat

Пробел?

Прочитать очередной символ

Да

Пробел или конец

 

строки?

 

Нет

while

Да

Нет

Добавить символ к накопителю

Прочитать очередной символ

Рис. 72 – Усовершенствованный алгоритм побуквенного чтения фамилии

Достройка программы

В основу новой версии программы P_31_1 положим программу P_30_1. Вам следует, прежде всего, открыть её и сохранить под новым именем. Готово? Тогда приступаем к правке.

Начнем с главной программы, где надо изменить имена входных и выходных файлов (чтобы не путать с похожими файлами предыдущей версии).

Assign(InFile,'Journal2.in'); Reset(InFile);

Assign(OutFile,'Journal2.out'); Rewrite(OutFile);

Позаботьтесь о том, чтобы файл «Journal2.in» был похож на настоящий классный журнал с фамилиями, как о нем сказано в начале 30-й главы.

212

Глава 31

Финал журнальной истории

Второе изменение внесем в процедуру обработки строки HandleString. Здесь объявим еще одну переменную строкового типа, назовем её Fam, она будет вмещать фамилию ученика.

Fam:= ReadFam; { читаем фамилию }

Разумеется, оператор печати строки тоже будет изменен.

Writeln(OutFile, Counter:3, Fam:18, Cnt:8,

Sum:14, Rating:11:1);

Осталось выяснить, что такое ReadFam? Это функция чтения фамилии, которую мы напишем по рассмотренному чуть выше алгоритму (рис. 72). Мой вариант функции таков.

function ReadFam: string;

var sym: char;

{ очередной символ }

S : string;

{ накопитель строки }

begin

 

S:=''; { очистка накопителя строки } { чтение символов до первой буквы }

repeat Read(InFile, sym); until Ord(sym)>32; { чтение последующих символов }

while not Eoln(InFile) and (Ord(sym)>32) do begin

S:= S+sym;

Read(InFile, sym);

end;

 

ReadFam:= S;

{ возвращаемый результат }

end;

 

Обратите внимание на сравнение введенного символа с пробелом. Это сравнение можно было бы записать так.

sym <> ’ ’

Но пробел в кавычках трудно разглядеть. Лучше сравнивать код символа с кодом пробела (который равен 32), что и сделано внутри функции.

Испытание

Теперь все готово, запустите программу. Что оказалось в выходном файле

«Journal2.out»? Наверное, вот это.

213

 

 

 

 

 

Глава 31

 

 

 

 

 

 

 

Финал журнальной истории

 

 

 

 

 

 

 

 

 

 

 

Номер

Фамилия

 

Количество

Сумма

Средний

 

 

ученика

 

 

 

оценок

баллов

балл

 

 

1

Акулова

 

3

12

4.0

 

 

 

2

Быков

4

 

20

5.0

 

 

 

3

Волков

4

 

18

4.5

 

 

 

4

Галкина

 

3

10

3.3

 

 

 

5

Крокодилкин

 

 

2

7

3.5

 

 

 

 

 

 

 

 

 

 

Если не считать кривых колонок, неплохо. Кривизну даёт разная длина фамилий учеников. Можно выровнять колонки, вычисляя спецификатор ширины в зависимости от длины фамилии. Или поступить иначе, — дополнить фамилии до одинаковой длины пробелами справа, например:

while Length(Fam) < 12 do Fam:= Fam + Char(32);

Этот оператор уместен после чтения фамилии. Окончательный вариант программы со всеми дополнениями и уточнениями представлен ниже.

 

{ P_31_1 – Обработка классного журнала, второй этап }

var

InFile, OutFile : text; { входной и выходной файлы }

 

Counter: integer;

{ счетчик строк в файле }

{-----

Функция чтения фамилии -----}

function ReadFam: string; var sym: char;

S : string; begin

s:=''; { очистка накопителя строки } { чтение символа до первой буквы }

repeat Read(InFile, sym); until Ord(sym)>32; { чтение последующих символов }

while not Eoln(InFile) and (Ord(sym)>32) do begin S:= S+sym;

Read(InFile, sym); end;

ReadFam:= S; end;

214

 

 

Глава 31

 

 

 

Финал журнальной истории

 

 

 

 

 

{----- Процедура обработки строки -----}

 

 

procedure HandleString;

 

 

var N : integer;

{ оценка, прочитанная из файла }

 

 

Cnt: integer;

{ количество оценок }

 

 

Sum: integer;

{ сумма баллов }

 

 

Rating: Real;

{ средний балл }

 

 

Fam: string;

{ фамилия }

 

 

begin

 

 

 

Fam:= ReadFam; { читаем фамилию }

 

 

{ для выравнивания столбцов добавляем пробелы }

 

 

while Length(Fam) < 12 do Fam:= Fam + ' ';

 

 

Sum:=0; Cnt:=0;

{ очищаем накопитель и счетчик оценок }

 

 

While not Eoln(InFile) do begin

{ пока не конец строки }

 

Read(InFile, N);

{ читаем оценку в переменную N }

 

Sum:= Sum+N;

{ накапливаем сумму баллов }

 

Cnt:= Cnt+1;

{ наращиваем счетчик оценок }

 

end;

 

 

 

 

 

 

if Cnt>0

 

 

 

 

 

 

then begin

 

{ если оценки в четверти были }

 

 

Rating:= Sum / Cnt;

{ вычисляем и печатаем ср. балл }

 

Writeln(OutFile, Counter:3, Fam:18, Cnt:8,

 

 

end

 

 

Sum:14, Rating:11:1);

 

 

 

 

 

 

 

else

{ а если оценок не было }

 

 

end;

Writeln(OutFile, ' Ученик не аттестован');

 

 

 

 

 

 

 

begin

{--- Главная программа ---}

 

 

 

 

Counter:= 0;

{ обнуляем счетчик строк }

 

 

 

{ открываем входной файл }

 

 

 

 

Assign(InFile,'Journal2.in'); Reset(InFile);

 

 

 

{ создаем выходной файл }

 

 

 

 

Assign(OutFile,'Journal2.out');

Rewrite(OutFile);

 

 

{ выводим шапку таблицы }

 

 

 

 

Writeln(OutFile, 'Номер

Фамилия

Количество

Сумма

Средний');

 

Writeln(OutFile, '

 

оценок

баллов

балл');

215