1. Тип файла
2. Обработка последовательного файла
2.1. Генерация файла
2.2. Визуализация файла
2.3. Корректировка последовательного
файла
3. Прямой доступ к файлам
4. Текстовые файлы
Файлы в некотором отношении похожи на массивы, но их элементы располагаются на ВЗУ. Специфика работы с файлами состоит в необходимости выполнять процедуры, обеспечивающие связь программы с файлом, который находится под управлением операционной системы (процедуры открытия и закрытия файла), а также значительно большее время доступа к записям, для реализации которого служат специальные процедуры ввода-вывода. Кроме того, исторически многие ВЗУ обеспечивали только строго последовательный доступ к элементам файла. Именно эти особенности легли в основу построения типа файлов в Паскале.
Описание типа: file of <Имя или описание базового типа (типа элементов файла)>
Имя переменной файлового типа принято называть логическим именем файла. Именно это имя используется в программе при обращении к его элементам. Кроме того, у файла есть имя, под которым он фигурирует в операционной системе (ОС) - физическое имя. Установление связи "логическое имя - физическое имя" выполняется специальной процедурой (Assign). Тех, кому такая система имён кажется неудобной, можно переубедить тем фактом, что одно логическое имя можно связывать в разное время с разными физическими, а это означает, что программу можно написать так, что её не надо будет переделывать при смене имени файла в ОС или при работе с другим файлом.
Операции с файлами реализуются обращением к стандартным процедурам:
Assign(F, FName) | Связывает логическое имя файла F с его физическим именем FName |
Reset(F) | Открывает файл F для ввода |
Rewrite(F) | Открывает файл F для вывода |
Close(F) | Закрывает файл F |
Read(F,R) | Чтение элемента файла F в переменную R |
Write(F,R) | Запись элемента файла А из переменной R |
Eof(F) | Возвращает булевское значение "Конец файла" |
Элемент файла обычно называют записью. При операциях с файлом предполагается, что открыть его можно только для ввода (Reset), или только для вывода (Rewrite). Если открываемый для вывода файл уже существовал, то старое его содержимое теряется. Первая операция ввода читает первую запись, вторая - вторую, и т.д. Аналогично первая операция вывода создаёт первую запись, вторая - вторую, и т.д. В этой связи говорят о последовательном характере доступа при работе с файлами. После окончания работы с файлом он должен быть закрыт.
Пример. Копирование файла.
program CopyFile; var FIn,FOut: file of Integer; Buf: Integer; begin Assign(FIn, 'DataIn.dat'); Reset(FIn); Assign(FOut, 'DataOut.dat'); Rewrite(FOut); while not Eof(FIn) do begin Read(FIn, Buf); Write(FOut, Buf); end; Close(FIn); Close(FOut); end.
Под генерацией понимается создание файла. Пример.
program GenFile; const KMAX = 'ZZ-ZZ'; type TRec = record Tag: Char; Key: string[5]; Info: string[10]; end; var Buf: TRec; FOut: file of TRec; FName: string[12]; begin Write('Name of file: '); ReadLn(FName); Assign(FOut, FName); Rewrite(FOut); repeat Write('Tag: '); ReadLn(Buf.Tag); Buf.Tag := UpCase(Buf.Tag); if Buf.Tag = 'E' then begin Buf.Key := KMAX; Buf.Info := ''; end else begin Write('Key: '); ReadLn(Buf.Key); Write('Info: '); ReadLn(Buf.Info); end; Write(FOut, Buf) until Buf.Tag = 'E'; Close(FOut); end.
Последняя запись созданного файла будет в поле ключа содержать максимальное значение превышающее все возможные реальные ключи - KMAX. Наличие такой записи нельзя считать обязательным, но во многих случаях приводит к упрощению алгоритмов обработки файлов (используется метод барьера).
Речь идёт о выводе файла в виде удобном для восприятия его записей человеком, т.е. о преобразовании его в текстовый вид. В примере данные выводятся на экран монитора.
program ViewFile; const KMAX = 'ZZ-ZZ'; type TRec = record Tag: Char; Key: string[5]; Info: string[10]; end; var Buf: TRec; FIn: file of TRec; FName: string[12]; begin Write('Name of file: '); ReadLn(FName); Assign(FIn, FName); Reset(FIn); Read(FIn, Buf); while Buf.Key < KMAX do begin with Buf do WriteLn(Tag, Key:6, ' ', Info); Read(FIn, Buf); end; Close(FIn); end.
Задача корректировки файла состоит в изменении состава его записей в соответствии с инструкциями. Основные инструкции - это включение новой записи (I), удаление старой записи (D) и замена старой записи новой (R). Инструкции удобно свести в один файл - файл корректировки. Удобно, чтобы структура записей файла корректировки совпадала со структурой корректируемого файла, или, как говорят, главного файла. Поле Tag будет играть роль кода инструкции. Другие поля суть значения полей новой записи или записи замены. Для инструкции удаления поле Info вообще не нужно, но его лучше оставить, чтобы форма всех инструкций была одинаковой. Далее мы приходим к идее упорядочить записи в главном файле и в файле корректировки по ключу. Это даёт возможность с учётом последовательного доступа к записям файла выполнить любую корректировку всего за один просмотр файлов. Алгоритм корректировки достаточно сложен. Для его упрощения изобрели некоторые приёмы, которые основаны на идее метода барьера. Роль барьеров играют специальные ключи (KMAX), такие, что реальные ключи не могут превосходить их значения. Такой ключ-барьер размещается в последней дополнительной записи каждого файла. Следующий пример весьма поучителен. Это особенно становится ясным, если вы, прежде чем приступите к его изучению, попытаетесь написать такую программу самостоятельно.
program Update; const KMAX = 'ZZ-ZZ'; type TRec = record Tag: Char; Key: string[5]; Info: string[10]; end; var BufM, BufU: TRec; FInM, {Главный файл} FInU, {Файл корректировки} FNew: {Скорректированный главный файл} file of TRec; FNameM, FNameU, FNameN: string[12]; begin Write('Name of main-file: '); ReadLn(FNameN); Write('Name of update-file: '); ReadLn(FNameU); Write('Name of new main-file: '); ReadLn(FNameN); Assign(FInM, FNameT); Reset(FInM); Assign(FInU, FNameU); Reset(FInU); Assign(FNew, FNameN); Rewrite(FNew); Read(FInM, BufM); Read(FInU, BufU); while (BufU.Key < KMAX) or (BufM.Key < KMAX) do begin case BufU.Tag of 'E': begin Write(FNew, BufM); Read(FInM, BufM) end; 'I': if BufU.Key >= BufM.Key then begin Write(FNew, BufM); Read(FInM, BufM) end else begin Write(FNew, BufU); Read(FInU, BufU) end; 'D': if BufU.Key > BufM.Key then begin Write(FNew, BufM); Read(FInM, BufM) end else if BufU.Key = BufM.Key then begin Read(FInM, BufM); Read(FInU, BufU) end else Read(FInU, BufU); 'R': if BufU.Key > BufM.Key then begin Write(FNew, BufM); Read(FInM, BufM) end else If BufU.Key = BufM.Key then begin Write(FNew, BufU); Read(FInM, BufM); Read(FInU, BufU); end else Read(FInU, BufU); end; {case} end; {While} Write(FNew, BufM); Close(FNew); Close(FInU); Close(FInM); end.
Пример входных и выходных данных
Main.dat:
* 22-34 Tom
* 33-17 Dick
* 44-07 Kate
Update.dat:
D 22-34
I 22-48 Bess
R 33-17 Sam
D 44-07
I 51-12 Phil
NewMain.dat:
I 22-48 Bess
R 33-17 Sam
I 51-12 Phil
Следует принять во внимание, что с целью придания последней программе большей иллюстративности в неё не включена печать протокола обновления, что на практике совершенно необходимо. Кроме того, программа никак не реагирует на ситуации, когда в обновляемом файле нет соответствия ключам инструкций D и R из файла корректировки. Такие инструкции в программе Update просто пропускаются без обработки, хотя на практике такие случаи должны фиксироваться в протоколе обновления.
В языке Turbo Pascal существует возможность прямого доступа к записям файла, который находится на жёстком диске. Для этого предусмотрены дополнительные процедуры:
FileSize(F) | Возвращает количество записей в файле F |
Seek(F, NRec) | Позиционирует указатель текущей записи
в файле F на запись с номером NRec ( NRec in [ 0..FileSize(F) - 1 ] ) |
Пример. Чтение записей файла в обратном порядке.
program RandAcc; var FIn: file of Integer; NRec,Buf,i : Integer; begin Assign(FIn, 'DataIn.dat'); Reset(FIn); NRec := FileSize(FIn); for i:= NRec-1 downto 0 do begin Seek(FIn, i); Read(FIn, Buf); WriteLn(Buf); end; Close(FIn); end.
Элементы текстовых файлов имеют тип Char. Включение управляющих символов CR ("перевод каретки") и LF ("перевод строки") создаёт в файле строковую структуру.
Описание типа: Text;
Для работы с текстовыми файлами предусмотрены процедуры, многие из которых нам уже хорошо знакомы:
Eof(F) | Возвращает булевское значение "Конец файла" для файла F |
Eoln(F) | Возвращает булевское значение "Конец строки" для файла F |
Write(F, <Список вывода>) | Вывод данных из списка в файл F |
Read(F, <Список ввода>) | Ввод данных из списка из файла F |
WriteLn(F, <Список вывода>) | Вывод данных из списка в файл F с переводом строки после вывода |
WriteLn(F) | Перевод строки ( включение в файл маркера конца строки CR/LF) |
ReadLn(F, <Список ввода>) | Ввод данных из списка из файла F с перходом на новую строку после ввода |
ReadLn(F) | Переход на новую строку |
Для данных из списка ввода-вывода автоматически выполняется преобразование "последовательность литер - внутреннее представление". Отметим, что для ранее рассматриваемых файлов, которые в отличие от текстовых называют типированными, никакого преобразования данных в ходе выполнения операций ввода-вывода не выполняется - потому они также называются бинарными.
Пример. Программа иллюстрирует применение функций Eof и Eoln.
program TextTst; var FTxt: Text; Ch: Char; Code: Integer; begin Assign(FTxt, 'Data.txt'); Reset(FTxt); WriteLn(Ord(Eof(FTxt)), Ord(Eoln(FTxt)):2); while not Eof(FTxt) do begin Read(FTxt, Ch); Code := Ord(Ch); if Code < 32 then Ch := ' '; WriteLn(Ord(Eof(FTxt)), Ord(Eoln(FTxt)):2, Ch:3, Code:4) end; end.
Data.txt:
12
ABC
END
Экран монитора:
0 0
0 0 1 49
0 1 2 50
0 0 13
0 0 10
0 0 A 65
0 0 B 66
0 1 C 67
0 0 13
0 0 10
0 0 E 69
0 0 N 78
0 1 D 68
0 0 13
1 1 10
Стандартные текстовые файлы, используемые в процедурах ввода-вывода по умолчанию:
Input | Клавиатура (Ctrl+Z для возбуждения EOF) |
Output | Экран монитора |
Явное указание имён этих файлов может использоваться для перенаправления ввода-вывода.
Пример. Перенаправление ввода.
program InpRedir; var Ch: Char; begin Assign(Input, 'Data.txt'); Reset(Input); while not Eof(Input) do begin Read(Ch); Write(Ch); end; end.
Data.txt:
AB
CDE
Экран монитора:
AB
CDE
Пример. Перенаправление вывода.
program OutRedir; var Ch: Char; begin Assign(Output, 'Data.txt'); Rewrite(Output); while not Eof(Input) do begin Read(Ch); Write(Ch); end; end.
Клавиатура:
abc<Enter>
12345<Enter>
<Ctrl+Z><Enter>
Data.txt:
abc
12345
Copyright г Барков Валерий Андреевич, 2000