Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
delphi / песни о паскале.pdf
Скачиваний:
62
Добавлен:
26.03.2016
Размер:
5.16 Mб
Скачать

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

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

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

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

var sym : char;

. . .

Read(InFile, sym);

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

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

S:= S + sym;

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

210

Глава 31

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

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

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

Repeat

Конец строки?

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

Нет Да

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

Да

Нет

Прочитана буква?

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

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

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

Примечание. Лишние пробелы в конце строк (после оценок) тоже приведут к аварии программы, проследите, чтобы во входном файле их не было. Проблема концевых пробелов решается заменой вызовов функций Eoln и Eof соответственно вызовами функций SeekEoln и SeekEof. Эти функции дают TRUE, даже если между текущей позицией и концом строки (файла) располагаются несколько пустых символов: пробелов, табуляций или пустых строк (для SeekEof). Улучшить программу P_31_1 предлагаю вам самостоятельно.

211

Глава 31

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

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

 

 

 

Repeat

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

 

 

файла?

 

 

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

 

 

 

Да

Нет

Repeat

Конец строки?

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

Нет

Да

Прочитана буква?

 

 

 

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

 

Да

Нет

Рис. 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) or Eof(InFile); { чтение последующих символов }

repeat

if Ord(sym) > 32 then s:= s+sym; if Eoln(InFile) then Break; Read(InFile, sym);

until Ord(sym) <= 32;

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) or Eof(InFile); { чтение последующих символов фамилии }

repeat

if Ord(sym) > 32 then s:= s+sym; if Eoln(InFile) then Break; Read(InFile, sym);

until Ord(sym) <= 32; ReadFam:= S;

end;

214

 

 

 

 

 

 

Глава 31

 

 

 

 

 

 

 

 

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

 

 

 

 

 

 

 

 

 

 

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

 

 

 

 

procedure HandleString;

 

 

 

 

 

 

var

 

 

 

 

 

 

 

 

 

N

: integer;

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

 

 

 

Cnt: integer;

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

 

 

 

 

Sum: integer;

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

 

 

 

 

 

Rating: Real;

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

 

 

 

 

 

Fam: string;

 

{ фамилия }

 

 

 

 

 

begin

 

 

 

 

 

 

 

 

 

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

 

 

 

 

 

if Length(Fam)>0 then begin { если фамилия не пуста, обрабатываем }

 

 

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

 

 

 

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, Sum:14, Rating:11:1);

 

 

 

end

 

 

 

 

 

 

 

 

 

else

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

 

 

 

 

end

Writeln(OutFile, Counter:3, Fam:18,' : Ученик не аттестован');

 

 

 

 

 

 

 

 

 

 

 

end;

 

 

 

 

 

 

 

 

 

begin

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

 

 

 

 

Counter:= 0; { обнуляем счетчик строк }

 

 

 

 

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

 

 

 

 

 

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

 

 

 

 

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

 

 

 

 

 

 

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

 

 

 

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

 

 

 

 

 

 

Writeln(OutFile, 'Номер

Фамилия

Количество

Сумма

Средний');

 

 

Writeln(OutFile, '

 

оценок

баллов

балл');

 

 

 

 

 

 

 

 

 

 

 

215

Соседние файлы в папке delphi