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

Глава 30

Журнальная история

Счетчик оценок = 0,

Накопитель суммы = 0

Пока не конец строки входного файла

while not Eoln

Прочитать оценку, Нарастить счетчик оценок,

Нарастить накопитель суммы

Счетчик оценок равен нулю?

Нет

 

 

Да

 

 

 

 

 

Вычислить средний балл

 

Записать в выходной

и записать результаты в

 

файл

выходной файл

 

«Ученик не аттестован»

 

 

 

 

 

 

 

 

 

 

Рис. 69 – Блок-схема обработки одной строки входного файла

Основу алгоритма составляет цикл чтения чисел, в ходе которого наращиваются счетчик оценок и их сумма. На входе в цикл и счетчик, и сумма очищаются, то есть загружаются нулями. Выход из цикла происходит при достижении конца строки, и тогда в бой вступает условный оператор. Он исследует счетчик оценок, и, если оценок в строке не было, выводит в выходной файл сообщение «ученик не аттестован», а иначе печатает средний балл.

Кстати, годится ли для чтения строки оператор цикла REPEAT-UNTIL? Правильный ответ — нет. Если текущая строка окажется пустой, следующая оценка прочитается уже на следующей строке, а это не то, что нам нужно! Оператор WHILE — единственно правильное решение.

Барабаним по клавишам

Теперь всё готово для сочинения задуманной программы P_30_1, ниже показан её текст. В начале программы объявлены три глобальные переменные: две

— для доступа к входному и выходному файлам, и одна — для подсчета читаемых строк. Поскольку эти переменные объявлены перед процедурой обработки строки HandleString, то будут видны и в этой процедуре. Поэтому передавать содержащиеся в них данные через параметры здесь не обязательно (но возможно).

201

Глава 30

Журнальная история

Таким образом, мы передаем данные в процедуру через глобальные переменные,

— в небольших программах это допустимо. Только не злоупотребляйте этим приемом, иначе в сложных программах запутаетесь.

Заглянем теперь внутрь процедуры HandleString. Кстати, её название составлено из двух слов: Handle — «обработка», и String — «строка». Процедура не принимает параметров, поскольку все необходимые данные получает через глобальные переменные. Обратите внимание на вычисление среднего балла:

Rating:= Sum div Cnt;

Сумма баллов делится на счетчик оценок; в этой операции участвуют целочисленные переменные, а результат деления тоже получится целым. Стало быть, дробную часть рейтинга мы теряем, — к решению этой проблемы вернемся позже.

А что сказать о главной программе? Она работает по ранее рассмотренному алгоритму. По крайней мере, сейчас нам так кажется.

{ P_30_1 – обработка журнала, первый вариант }

 

{----- Глобальные переменные -----}

var

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

 

Counter: integer;

{ счетчик строк входного файла }

 

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

procedure HandleString;

 

var

N : integer;

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

 

Cnt: integer;

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

 

Sum: integer;

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

begin

Rating: integer;

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

Sum:=0; Cnt:=0; { очищаем накопитель и счетчик оценок }

 

 

while not Eoln(InFile) do begin { пока не конец строки }

Read(InFile, N);

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

Sum:= Sum+N;

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

Cnt:= Cnt+1;

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

end;

 

202

Глава 30

Журнальная история

if Cnt>0

then begin { если оценки были } Rating:= Sum div Cnt;

Writeln(OutFile, Counter, Cnt, Sum, Rating);

end

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

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

end;

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

 

 

 

begin

 

 

 

 

 

 

Counter:= 0;

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

 

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

 

 

 

 

Assign(InFile,'P_30_1.in');

Reset(InFile);

 

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

 

 

 

 

Assign(OutFile,' P_30_1.out');

Rewrite(OutFile);

 

 

 

 

 

 

 

 

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

 

 

 

 

Writeln(OutFile, 'Номер

Количество

Сумма

Средний');

Writeln(OutFile, 'ученика

оценок

баллов

балл');

{ пока не конец входного файла... }

 

 

 

while not Eof(InFile) do begin

 

 

 

Counter:= Counter+1;

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

HandleString;

{ обрабатываем строку }

 

end;

 

 

 

 

 

 

{ закрываем оба файла }

 

 

 

 

Close(InFile);

Close(OutFile);

 

 

 

end.

 

 

 

 

 

 

Скомпилировали программу? Тогда подготовьте входной файл с оценками учеников (без фамилий). Назовите его «P_30_1.in» и сохраните, как обычно, в рабочем каталоге. Можно запускать? В общем-то, да. Но будьте настороже, — ошибки караулят нас на каждом шагу! Возьмите за правило первый прогон своих программ выполнять в пошаговом режиме. Поступим так и в этот раз.

Первый блин

Переключитесь в окно программы и для исполнения одного шага нажмите клавишу F7. Продолжайте нажимать её, наблюдая за ходом выполнения операторов. При желании добавьте в окно обзора переменных «Watch» глобальную переменную Counter.

203

Глава 30

Журнальная история

После нескольких шагов вы попадете внутрь процедуры HandleString и благополучно пройдете по ней. На следующем цикле главной программы вновь войдете туда же, но теперь вас ждут «чудеса». Во-первых, цикл

while not Eoln(InFile)

уже не выполняется ни разу, и в выходной файл пишется «Ученик не аттестован». Ещё загадочней другой фокус: не выполняется условие выхода из цикла в главной программе.

while not Eof(InFile)

Обработав несколько строк, программа почему-то продолжает фанатично работать и дальше; она, как говорится, зациклилась. Если бы мы запустили её сразу в непрерывном режиме, то захламили бы выходным файлом весь диск! Пришлось бы аварийно снимать программу диспетчером задач. Ясно, что где-то притаилась ошибка, и надо прервать выполнение программы. Сделать это в пошаговом режиме несложно, — нажмите комбинацию Ctrl+F2.

Теперь сядем на пенёк и поразмыслим, в чем дело? Ведь первый вход в процедуру отработан верно. Вспомните наше исследование функции Eoln: чтение последнего числа в строке устанавливает признак конца строки в TRUE. Значит, при повторном входе в процедуру обработки строки цикл while not Eoln(InFile) не должен выполняться ни разу, — так оно и происходит. Так вот где собака порылась, — мы застряли в конце первой строки!

Этим же объясняется и зацикливание в главной программе, — не проскочив первой строки, нам не достичь конца файла. Виновник найден? — да, и теперь поищем решение проблемы. После обработки строки нам надо всего лишь перескочить с конца текущей строки в начало следующей, ничего при этом не читая. На ум приходит процедура Readln, — ведь она любит это делать. Помните, как подвела она нас во второй версии полицейской базы данных? Зато теперь выручит! Где её вызвать? Сделаем это после выхода из процедуры HandleString, то есть в цикле главной программы. Теперь главный цикл будет выглядеть так.

while not Eof(InFile) do begin

Counter:= Counter+1;

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

HandleString;

{ обрабатываем строку }

Readln(InFile);

{ переход на следующую строку }

end;

 

Процедуре Readln передана лишь файловая переменная InFile, поскольку никаких данных ей читать не надо. Стало быть, для исправления программы добавим лишь один оператор. Сделайте это и запустите программу в пошаговом

204

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