
- •4. Текстовые файлы и циклическое выполнение
- •4.1 Текстовые файлы
- •4.1.1. Операции над файлами.
- •4.1.2. Синтаксис и семантика файлов.
- •4.1.3. Копирование файлов
- •4.1.4. Разделение файлов.
- •4.2. Сортировка и использованием циклического выполнения.
- •4.2.1. Программа SelectSort
- •4.2.2. Структурное тестирование
- •4.2.3. Анализ SelectSort
- •4.3. Маркеры текстовых файлов
- •4.3.1. Маркер конца строки
- •4.3.2. Маркер конца файла
- •4.3.3. Копирование строк
- •Dp4.4 { Копируем f2 в f1 } и dp4.3 { Копируем f1 в output }
- •4.4. Заключение
4.3.2. Маркер конца файла
В Паскале имеется стандартное условие EOF, которое позволяет узнать, есть ли возможность считать еще какие-то данные из файла. EOF принимает значение TRUE, если курсор находится за последним элементом данных (например за маркером конца последней строки в текстовом файле), в иных случаях EOF принимает значение FALSE. Для файла F1 условие записывается как EOF(F1). Например, если F1 содержит одну строку, EOF будет принимать следующие значения:
-
Ch
F1
EOF
READ(Ch)
READ(Ch)
?
A
A/
A/
A/_
FALSE
FALSE
TRUE
Необходимо заметить, что различные реализации Паскаль-машины по разному интерпретируют символ конца строки и стоящие перед ним пробелы. Некоторые реализации, например, удаляют все пробелы, которые встречаются между последним не пробелом и концом строки, символ конца строки также может заменяться или не заменяться на пробел.
Таким образом мы не вполне можем контролировать эту ситуацию, в зависимости от реализации Паскаль-машины, пробелы в конце строки могут пропадать или наоборот появляться.
SR23
<условие> ::= <выражение> <оператор сравнения> <выражение>
| NOT (<уcловие>)
| (<уcловие>) AND (<уcловие>)
| (<уcловие>) OR (<уcловие>)
| EOF
| EOF (<идентификатор>)
| EOLN
| EOLN (<идентификатор>)
CR6
Идентификатором типа TEXT является <идентификатор> описанный в разделе объявлений имеющий <тип> TEXT. Если идентификатор типа TEXT появляется в <выражении WRITE> или в <выражении READ>, он должен стоять первым в <списке идентификаторов> или <списке вывода>. Идентификатор типа TEXT, иной чем INPUT или OUTPUT должен быть описан в <объявлениях>. Только идентификаторы типа TEXT, исключая INPUT и OUTPUT, могут появляться внутри выражений RESET и REWRITE. Только идентификаторы типа TEXT, к которым было применено выражение RESET, могут появляться в <условиях> EOF и EOLN.
4.3.3. Копирование строк
Когда INPUT содержит набор строк, для перемещения через границу строки может использоваться выражение READLN. В результате выполнения выражения READLN , курсор будет перемещен за ближайший маркер конца строки. Если маркер конца строки не встретится, READLN возвратит ошибку. READLN (F1) будет выполняться для файла F1.
READLN(Ch1, Ch2, Ch3)
Будет эквивалентно
READ(Ch1);
READ(Ch2);
READ(Ch3);
READLN
SR12.
<выражение READ> ::= READ(<список идентификаторов>)
| RESET(<идентификатор>)
| READLN(<список идентификаторов>)
Значение условий EOLN и EOF и поведение выражений READ и READLN определены только для ситуаций, когда они выполняются до прохождения соответствующих их границ. Например значение EOF не определено за границами файла. Поэтому важно правильно использовать операторы чтения файла и определения границ, например по шаблону, предлагаемому следующей программой:
PROGRAM CopyLines (INPUT, OUTPUT);
{Копирует INPUT в OUTPUT сохраняя структуру строк}
VAR
Ch: CHAR;
BEGIN {CopyLines}
WHILE NOT EOF
DO
BEGIN {Копировать одну строку}
WHILE NOT EOLN
DO
BEGIN {Копировать один символ}
READ(Ch);
WRITE(Ch);
END;
READLN;
WRITELN
END
END. {CopyLines}
Внешнее выражение WHILE защищает внутренний код от ситуации выхода за конец файла. Внутреннее выражение WHILE защищает его оператор READ от чтения маркера конца строки. После того как внутренний WHILE скопировал строку, с помощью READLN курсор перемещается за маркер конца строки, а в OUTPUT создается маркер конца строки с помощью WRITELN. Детали в следующей таблице выполнения:
|
Ch |
INPUT |
EOF |
EOLN |
OUTPUT |
WHILE NOT EOF WHILE NOT EOLN READ(Ch) WRITE(Ch) WHILE NOT EOLN READLN WRITELN WHILE NOT EOF WHILE NOT EOLN READ(Ch) WRITE(Ch) WHILE NOT EOLN READLN WRITELN WHILE NOT EOF |
?
A
B |
A/B/
A/B/
A/B/
A/B/
A/B/_
|
FALSE
FALSE
FALSE
FALSE
TRUE |
FALSE
TRUE
FLASE
TRUE
? |
_
A_
A/_
A/B_
A/B/_ |
Ранее мы использовали в качестве маркера конца последовательности символ # и копирование файлов выполняли по примерно следующему образцу:
READ(F1, Ch);
WHILE Ch <> ‘#’
DO
BEGIN
WRITE(F2, Ch);
READ(F1, Ch)
END
Если мы используем маркер конца строки, то получаем следующий образец:
WHILE NOT EOLN(F1)
DO
BEGIN
READ(F1, Ch)
WRITE(F2, Ch);
END
Второй вариант более соответствует структуре текстовых файлов и более естественно реализует операцию копирования.
4.3.4. BubleSort
SelectSort сортируя файлы длины N выполянет порядка 2N2 выражений READ/WRITE. Возможна ли более быстрая сортировка? IFSort и MinSort достаточно эффективны при сортировке строк фиксированной длины, делая работу в один проход и выполняя порядка 2N выражений READ/WRITE. Поскольку 2N2 всегда больше 2N при всех N > 1, вероятно существуют пути сортировать быстрее чем SelectSort. Новый алгоритм сортировки также нам позволит попрактиковаться в работе с границами файлов с помощь EOF.
Если INPUT уже отсортирован, SelectSort все равно потребует 2N2 выражений READ/WRITE. Это наблюдение предлагает нам сделать проверку, не имеем ли мы дело с уже отсортированными данными.
BEGIN {Проверяем F1 на отсортированность}
Sorted := ‘Y’;
RESET(F1);
IF NOT EOLN(F1)
THEN
BEGIN
READ(F1, Ch1);
WHILE NOT EOLN(F1)
DO
BEGIN
READ(F1, Ch1);
IF Ch2 < Ch1
THEN
Sorted := ‘N”;
Ch1 : Ch2;
END
END
END
Программа перемещает по файлу окно в два символа и если в файле есть фрагмент, который не отсортирован, Sorted будет присвоено ‘N’. Почему бы не использовать этот эффект для сортировки?
F1 копируется в F2, но когда Ch2 < Ch1, эти два символа копируются в F2 в обратном порядке. Таким образом, после некоторого количества проходов по файлу не останется пар символов стоящим в обратном порядке.
Эта идея послужила основой для алгоритма BubleSort, названного так, потому что изменение позиций сортируемых символов напоминает всплывание пузырьков.
DP4
PROGRAM BubbleSort(INPUT,OUTPUT);
{Сортирует первую строку INPUT в OUTPUT}
VAR
Sorted,Ch,Ch1,Ch2:CHAR;
F1,F2:TEXT;
BEGIN {BubbleSort}
{Копируем INPUT в F1}
Sorted :='N';
WHILE Sorted ='N'
DO
BEGIN
{Копируем F1 в F2,проверяя отсортированность
и переставляя первые соседнии символы по порядку}
{Копируем F2 в F1}
END;
{Копируем F1 в OUTPUT}
END.{BubbleSort}
DP 4.1
BEGIN { Копируем F1 в F2,проверяя отсортированность
и переставляя первые соседнии символы по порядку}
Sorted:='Y';
RESET(F1);
REWRITE(F2);
IF NOT EOF(F1)
THEN
BEGIN
READ(F1,Ch1);
WHILE NOT EOLN(F1)
DO { По крайней мере два символа остается для Ch1,Ch2 }
BEGIN
READ(F1,Ch2);
{ Выводим min(Ch1,Ch2) в F2, записывая
отсортированные символы }
END;
WRITELN(F2,Ch1) { Выводим последний символ в F2 }
END
END
DP 4.1.1
{ Выводим min(Ch1,Ch2) в F2, записывая
отсортированные символы }
IF Ch1 <= Ch2
THEN
BEGIN
WRITE(F2,Ch1);
Ch1:=Ch2
END
ELSE
BEGIN
WRITE(F2,Ch2);
Sorted := 'N'
END
DP4.2
BEGIN { Копируем INPUT в F1 }
REWRITE(F1);
WHILE NOT EOLN
DO
BEGIN
READ(Ch);
WRITE(F1,Ch);
END;
WRITELN(F1)
END;
Разделы