22. Файлы

Паскаль-программа может работать с внешними файлами: читать из них ин­формацию, записывать информацию в файл, корректировать файлы, создавать новые файлы, переименовывать и уничтожать существующие файлы. Различают три типа файлов: текстовые, типизированные и бинарные. Это различие влияет лишь на способы обращения к файлу, один и тот же файл на диске программа может рассматривать и как текстовый, и как типизированный, и как бинарный. Рассмотрим сначала текстовые файлы.

Для работы с текстовым файлом в программе следует описать файловую пе­ременную типа TEXT :

VAR f : TEXT;

Прежде чем выполнять какие либо операции с файлом, необходимо инициа­лизировать файл, т.е. установить связь файловой переменной с файлом на диске процедурой

1. Assign(VAR/:TEXT; Name:String) , где Name - правильно построенное имя файла, существующего или вновь созда­ваемого. После этого выполняется открытие файла одной из трех процедур:

2. Reset(VAR /:TEXT) - открывает файл для чтения.

3. Rewrite(VAR /:TEXT) - открывает файл для записи.

4. Append(VAR /:TEXT) - открывает файл для записи в конец файла. Процедуры Reset и Append выполняются только для существующих файлов,

процедура Rewrite - для любых файлов, но если файл существует, он будет уничтожен и создан заново. Чтение из файла и запись в файл выполняются про­цедурами READ[LN] и WRITE[LN], но перед списком ввода или вывода задает­ся файловая переменная:

5. Read[Ln](VAR/:TEXT; список ввода).

6. Write[Ln](VAR /:TEXT;сnuсок вывода).

Списки ввода и вывода строятся точно так же, как и в случае ввода с клавиа­туры и вывода на экран. Особенностью текстовых файлов является то, что они состоят из строк, каждая из которых заканчивается символом конца строки. Процедура WriteLn записывает в файл этот символ, а процедура Write - нет. Вы можете сами управлять длинами записываемых строк, в нужный момент вызывая процедуру WriteLn. При вводе следует помнить, что если символ конца строки не считан процедурой ReadLn, то следующая строка недоступна. Как правило, текстовый файл используется для хранения строк или символов, но можно дер­жать там и числа. Для текстовых файлов определены четыре логические функ­ции:

7. Function EOLN(VAR /:TEXT):Boolean - возвращает TRUE, если при чте­нии достигнут конец строки.

8. Function EOF(VAR /:TEXT):Boolean - возвращает TRUE, если при чте­нии достигнут конец файла.

9. Function SeekEOLN(VAR /:TEXT):Boolean - возвращает TRUE, если в строке больше нет ничего, кроме пробелов.

10. Function SeekEOF(VAR /:TEXT):Boolean - возвращает TRUE, если в файле нет больше ничего, кроме пробелов.Функция EOLN пригодится вам, если вы читаете из текстового файла символы; функция EOF - если вы читаете симво­лы или строки, а функции SeekEOLN и SeekEOF необходимы при вводе чисел из текстового файла. Функции EOLN и SeekEOLN также могут быть полезны при обычном вводе с клавиатуры. Приведем пример : пусть необходимо ввести некоторый массив натуральных чисел и некоторый массив символов. Известно, что в обоих массивах не более 100 элементов. Запишем программу, которой не нужно заранее знать, сколько элементов будет введено, это удобнее, чем сначала запрашивать количество элементов в массиве.

CONST Nmax=100; VAR x : ARRAY[1..Nmax] OF Word; c : ARRAY[1..Nmax] OF Char;

i : Byte;

CONST nX : Byte = 0;

nC : Byte = 0;

BEGIN

WRITELN('Bведите числа');

{ вводим все числа, заканчивая клавишей Enter}

WHILE NOT SeekEOLN DO BEGIN INC(nX); Read(x[nX]); END;

{ считываем конец строки, иначе не введутся символы} READLN;

WRITELN('Bведите символы'); {вводим символы, заканчивая клавишей Enter}

WHILE NOT EOLN DO BEGIN INC(nC); Read(c[nC]); END;

WRITELN('Bведено ',nX,' чисел :');

FOR i:=1 TO nX DO WRITE(x[i]:в); WRITELN;

WRITELN('Bведено ',nC,' символов :');

FOR i:=1 TO nC DO WRITE(c[i]); WRITELN;

END.

Вернемся к работе с файлами. Текстовый файл, открытый для чтения, можно усекать процедурой

11. Procedure Truncate(VAR f:TEXT).

Эта процедура уничтожает весь непрочтенный остаток файла. Файл закрывается процедурой.

12. Procedure Close(VAR f:TEXT),

после чего файловую переменную можно использовать для других целей. Любой файл можно удалить с диска процедурой.

13. Procedure Erase(VAR f).

Удаляемый файл должен быть инициализирован, но не открыт, или открыт, но затем закрыт. Запишем программу, которая читает текстовый файл и выводит его на экран:

VAR f : TEXT; s : STRING;

CONST Name='test.pas';

BEGIN Assign(f,Name); Reset(f);

WHILE NOT EOF(f) DO BEGIN READLN(f,s); WRITELN(s); END;

END.

Теперь выполним то же самое, не используя строку: VAR f : TEXT; c : Char; CONST Name='test.pas'; BEGIN Assign(f,Name); Reset(f);

WHILE NOT EOF(f) DO BEGIN

WHILE NOT EOLN(f) DO BEGIN READ(f,c); WRITE(c); END;

READLN(f); WRITELN; END;

END.

Если в этой программе опустить READLN(f) , то она зациклится. Прочтем из текстового файла числа (конечно, в таком файле должны быть записаны только числовые константы и пробелы):

VAR f : TEXT; x : Real;

CONST Name='num.txt'; BEGIN Assign(f,Name); Reset(f);

WHILE NOT SeekEOF(f) DO BEGIN READ(f,x); WRITE(x:10); END;

Числа в текстовом файле могут быть записаны как в одной, так и в несколь­ких строках и разделяться любым количеством пробелов.

Второй тип файлов - типизированные файлы. Файловая переменная описы­вается в этом случае как

FILE OF тип ,

где тип - любой тип, кроме файлового. Считается, что типизированный файл содержит некоторое количество записей одного и того же типа.Читать и записы­вать в такой файл можно только данные этого типа. В отличие от текстовых фай­лов типизированные допускают прямой доступ, т.е. вы можете записывать в лю­бое место файла и читать из любого места файла. Процедура Assign применяется для типизированных файлов точно так же, как и для текстовых. Типизированный файл можно открыть процедурами Reset и Rewrite (процедура Append неприме­нима!), в обоих случаях файл доступен и для чтения, и для записи. Процедуру Reset следует применять для существующих файлов, а Rewrite - для новых фай­лов. Процедуры Truncate и Close работают точно так же, как и для текстовых файлов. Чтение и запись в типизированный файл осуществляется процедурами

READ и WRITE (READLN и WRITELN не имеют смысла). Но в списках ввода

и вывода можно записывать только переменные соответствующего типа. Функ­ция EOF применима для типизированных файлов, а EOLN - нет. Прямой доступ к файлу осуществляется с помощью процедур FileSize, FilePos и Seek.

14. PROCEDURE FileSize(VAR f): Longint - возвращает текущий размер файла в записях, т.е. размер файла в байтах можно получить, умножив эту вели­чину на размер одной записи.

15. PROCEDURE FilePos(VAR f): Longint - возвращает текущее значение файлового указателя. Файловый указатель хранит текущий адрес в файле, начи­ная с которого будет выполняться очередная операция ввода или вывода. При каждой операции ввода-вывода файловый указатель автоматически смещается на количество введенных или выведенных записей.

16. PROCEDURE Seek(VAR f; n: Longint); - устанавливает новое значение файлового указателя. Значение файлового указателя равно номеру последней обработанной записи, поэтому номер текущей записи будет равен n+1. Таким образом, чтобы установить указатель на первую запись, необходимо выполнить Seek(f,0), а на последнюю - Seek(f,FileSize(f)-1). Запишем программу, которая работает с типизированным файлом:

VAR f : FILE OF Real; i,n : Byte; r : Real;

BEGIN Assign(f,'TMP'); Rewrite(f);

Randomize; n:=Random(100)+1; { количество записей в файле } FOR i:=1 TO n DO BEGIN r:=Sqrt(i); WRITE(f,r); END; WRITELN('Размер файла=',FileSize(f));

i:=Random(n); Seek(f,i); READ(f,r);

WRITELN('Запись номер ',FilePos(f),' содержит ',r); Close(f);

END.

Третий тип файлов в Паскале - бинарные файлы. Бинарные файлы рассмат­риваются как последовательности байтов и могут содержать любые данные.

Файловая переменная в этом случае должна иметь тип FILE. Бинарный файл открывается процедурами:

17. PROCEDURE Reset(VAR f : FILE; RecSize: Word);.

18. PROCEDURE Rewrite(VAR f : FILE; RecSize: Word);.

Второй параметр RecSize - это длина записи в байтах. Ввод-вывод в бинар­ный файл можно осуществлять порциями, кратными длине записи. Если при от­крытии файла задать длину записи в 1 байт, то такой файл сможет содержать любые данные. Для бинарных файлов также определены процедуры Close, FileSize, FilePos и Seek..Но чтение и запись осуществляются специальными про­цедурами:

19. PROCEDURE BlockRead(VAR/FILE; VAR Buf; Count:Word

[;VAR Res:Word]);.

20. PROCEDURE BlockWrite(VARf:FILE; VAR Buf; Count:Word

[;VAR Res:Word]);.

Здесь Buf - любая переменная, Count - количество вводимых или выводимых за­писей, Res - выходной параметр, возвращающий количество введенных или вы­веденных записей. Последний параметр необязателен, но в некоторых случаях его использование очень полезно. Запишем предыдущую программу, используя бинарный файл:

VAR f : FILE; i,n : Byte; r : Real;

BEGIN Assign(f,'TMP'); Rewrite(f,SizeOf(REAL));

Randomize; n:=Random(100)+1; { количество записей в файле } FOR i:=1 TO n DO BEGIN r:=Sqrt(i); BlockWrite(f,r,1); END;

WRITELN('Размер файла=',FileSize(f)); i:=Random(n); Seek(f,i); BlockRead(f,r,1);

WRITELN('Запись номер ',FilePos(f),' содержит ',r); Close(f);

END.

В этой задаче мы не получили никаких преимуществ от использования би­нарного файла, т.к. файл содержал однотипные данные - числа типа Real, но би­нарные файлы могут содержать и данные разных типов вперемешку, тогда их использование очень эффективно. Решим еще одну задачу - быстро переписать содержимое одного файла (любого) в другой файл. Мы не будем интересоваться - что записано в файле, а лишь постараемся минимизировать количество обра­щений к файлам. VAR Source,Target : FILE;

Buf : ARRAY[1..64000] OF Byte; {большой массив - буфер}

Result     : Word; CONST SourceName='TEST.PAS';

TargetName-COPY.BIN'; BEGIN Assign(Source,SourceName); Reset(Source,1);

Assign(Target,TargetName); Rewrite(Target,1);

REPEAT

{ читаем из исходного файла как можно больше информации}

BlockRead(Source,Buf,SizeOf(Buf),Result);

{ записываем в новый файл столько, сколько удалось прочесть}

BlockWrite(Target,Buf,Result);

{ прекращаем чтение-запись, когда прочитано меньше байт, чем умещается в буфере, это

значит, что в исходном файле больше ничего нет}

UNTIL Result<SizeOf(Buf);

WRITE^'OK...^. файл "',TargetName,'"'); Close(Source); Close(Target);

END.