12.7. Типизированные файлы и операции ввода-вывода
Типизированный, или компонентный, файл — это файл с объявленным типом его компонентов, т.е. файл с наборами данных одной и той же структуры. Как и в стандарте Паскаля, объявление такого файлового типа имеет структуру
File of ТипКомпонента,
- 246 -
где компонент может иметь любой ординарный или структурированный тип, предопределенный или построенный программистом. Запрещено лишь объявлять файлы файлов и файлы объектов, а также файлы структурированных компонентов (массивов, записей и др.), содержащих те же файлы или объекты. Так, допустимы следующие объявления:
TYPE
DimComp = Array [1..100,1..2] of Real;
RecComp = RECORD
X,Y : Byte;
A : DimComp
END;
DimFile = File of DimComp;
RecFile = File of RecComp;
IntFile = File of Integer;
Но компилятор не пропустит такие типы:
TYPE
FileFilel = File of File of Real; { неверно: файл файлов }
FileFile2 = File of DimFile; { неверно: файл файлов }
FRecComp = RECORD
X,Y : Byte;
F : File of Char
END;
FRecFile = File of FRecComp; { нельзя: файл в компоненте!}
ObjComp = OBJECT
...
END;
ObjFile = File of ObjComp; { неверно: файл объектов }
При написании программ необязательно определять специальные файловые типы. Это можно сделать «на ходу» при описании переменных:
VAR
FR : File of Real;
FD : File of DimComp;
Для работы с объявленным файлом необходимы обычные предварительные действия: связывание файловой переменной с физическим файлом и открытие файла для чтения или записи, например:
Assign( FR, 'RFILE.DAT' ); Reset( FR );
Assign( FD, 'DFILE.DAT' ); Rewrite( FD );
- 247 -
Для типизированных файлов обе процедуры Reset и Rewrite устанавливают режим «чтение/запись» в соответствии со значением предопределенной системной переменной FileMode (оно по умолчанию равно 2), т.е. независимо от выбора процедуры открытия, файл открывается и для чтения, и для записи. Это верно только для типизированных и бестиповых файлов, но ни в коем случае, не для текстовых. Этот порядок нарушится только в том случае, когда значение FileMode соответствует режиму «только запись» (1) или «только чтение» (0). Сменить режим можно простым присваиванием нужного значения переменной FileMode перед открытием файла. После этого вызов Reset будет открывать файл в заданном режиме, даже если он будет «только запись»! Процедура Rewrite также должна «слушаться» указаний FileMode, но только в том случае, когда файл уже существует. Для новых файлов Rewrite всегда включает режим «чтение/запись».
На практике редко возникает необходимость вмешиваться в стандартный порядок работы процедур. Более того, лучше использовать те процедуры, которые более соответствуют смыслу программы.
После открытия файла ввод и вывод данных осуществляется стандартными операторами
Read( f, х ) и Write( f, x )
или
Read( f, х1, х2, хЗ,...,xn) и Write(f, х1, х2, хЗ,..., xn).
Первым аргументом должно быть имя логического файла f, с которым связан конкретный физический файл. А далее должна стоять переменная (или их список) того же типа, что и объявленный тип компонента файла f, в которую запишется очередное значение из файла при чтении (Read) или, наоборот, которая запишется в файл (Write).
В отличие от файлов типа Text типизированные файлы имеют более строгую внутреннюю структуру. При записи в них записывается машинное представление очередного компонента, будь то число, массив, запись или строка. Внутри файла компоненты не отделяются ничем друг от друга (тем не менее найти любой компонент несложно: каждый из них занимает в файле одинаковый объем, равный размеру его типа). Поэтому не имеет смысла применять к типизированным файлам операторы ReadLn и WriteLn. В них просто не существует такого понятия, как строка и ее конец, и нет признака конца файла (конец определяется длиной файла). Даже если объявить файл как
- 248 -
VAR
f : File of String[80];
он не будет похож на текстовый файл из строк. В нем по-прежнему не будет символов конца строки, а будет (после записи) сплошная последовательность символов, кратная 81 (80 символов в строке и байт длины строки). Хуже того, если реальные значения строк короче 80 символов, то все равно в файле окажется по 81 символу на строку, и «излишки» будут забиты «мусором»,
Поэтому любые попытки трактовать типизированный файл как текст, особенно попытки вывести его на экран или принтер, вызовут ошибки и непредсказуемые последствия.
Для специальных целей (например, написания программ перекодировки текстов) можно трактовать текстовые файлы как File of Char. Это, разумеется, не оживит работу процедур ReadLn/WriteLn и функции EOLn, работающей с текстами, но при соблюдении неприкосновенности и неизменности кодов конца строки и конца файла (символ #26), даст возможность побуквенного чтения, изменения и последующей записи в другой File of Char исходного текста. Каркас такой программы показан на рис. 12.7.
| PROGRAM Decode_Text;
| VAR
| fIn, fOut : File of Char; {файлы для чтения и записи}
| Ch : Char; {обрабатываемый символ }
| FUNCTION DecodeChar( С : Char ) : Char;
| { Функция должна возвращать символ,
| получающийся перекодировкой из C. }
| BEGIN { . . . } END;
| BEGIN
| Assign( fIn, 'C:DOCDOCUM.A' ); Reset( fIn );
| Assign( fOut, 'C:DOCDOCUM.B' ); Rewrite( fOut );
| Ch := ' '; { очистка Ch }
| while Ch <> #26 do
{цикл до конца ТЕКСТОВОГО файла:}
| begin
| Read ( fIn, Ch ); { читается символ }
| Write( fOut, DecodeChar(Ch) ) { запись замены }
| end; {while} { конец цикла }
| Close( fIn ); Close( fOut ) { закрытие файлов }
| END.
Рис. 12.7
- 249 -
(Чтобы не закладывать имена текстовых файлов в текст самой программы, следовало бы использовать функцию ParamStr или организовать более или менее «дружелюбный» диалог с пользователем, что советуем сделать.)
Преимущества типизированных файлов очевидны: они максимально эффективным способом хранят числовую информацию, позволяют считывать и записывать сложные и громоздкие структуры буквально одной командой, например:
TYPE
dim100x20 = Array [1..100, 1..20] of Real;
VAR
XX, YY : dim100x20;
fIn, Fout : File of dim100x20;
BEGIN
... { открытие файлов fin и fOut... }
Read( fIn, XX ); { считывается сразу весь массив }
... { обрабатываются массивы }
Write( fOut, YY ); { записывается весь массив сразу }
... { среди прочего — закрытие файлов}
END.
В то же время эти файлы неоптимальны для хранения строк (лучше использовать Text-файлы) и имеют сложное внутреннее представление. Последнее означает, что если забыть, что именно содержится в подобном файле, то его просмотр вряд ли поможет.
Размер буфера для типизированных файлов устанавливается автоматически, исходя из размера компонентов. Пользователю не предоставляется возможность изменить корректным способом его размер.