Текстовые файлы

1. Введение

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

2. Текстовые файлы

Файл – это совокупность логически объединённых данных. Физическая структура файла на диске довольно сложная, файл может храниться отдельными частями в разных местах диска, а в специальной таблице указывается, к какому файлу относится каждая из частей. Это явление называется фрагментацией файлов. Фрагментация замедляет доступ к данным, и с ней обычно борются с помощью процедуры дефрагментации.

Пользовательские текстовые файлы представляют собой отдельные от программы файлы текстового формата, т.е. они могут быть созданы, прочитаны и отредактированы любым текстовым редактором. Программа, в принципе, тоже является текстовым файлом, её тоже можно открыть и отредактировать текстовым редактором. Текстовый редактор входит и в состав систем разработки приложений, в частности, Borland Developer Studio. Входные и выходные файлы можно создавать и просматривать прямо в ней. Для создания текстового файла выберите пункт меню File – New – Other… – Other Files – Text.

Преимущества текстовых файлов:

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

Текстовый файл представляет собой совокупность строк (последовательностей символов) переменной длины, заканчивающихся специальным символом eoln – конец строки (на клавиатуре набирается нажатием клавиши «Ввод»). Файл заканчивается специальным символом eof – конец файла, который добавляется редактором автоматически.

структура файла

Каждая строка текстового файла является набором символов, но при определённых условиях может интерпретироваться как набор данных других типов. Например, строка
124 56.94
может быть преобразована в целое и вещественное число или в два вещественных числа.

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

Входные файлы создаются любым подходящим способом вне программы и до её запуска. Выходные файлы формируются программой.

3. Работа с текстовыми файлами в программе на языке Паскаль

При использовании файлов в программе файл представляется в двух аспектах:

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

Прежде чем файловая переменная может быть использована, она должна быть ассоциирована с файлом с помощью вызова процедуры AssignFile.
procedure AssignFile(var F: file; FileName: string);

При вызове этой процедуры ей передаются два параметра. Первый – это файловая переменная любого типа. Второй – строка, задающая имя файла на диске. Можно передавать полный путь к файлу, начиная с буквы логического диска, а можно использовать только имя файла и расширение – в этом случае файл будет искаться или создаваться в рабочей директории, которой по умолчанию является директория с файлами Delphi-проекта.

После вызова процедуры AssignFile файловая переменная ассоциируется с указанным файлом до вызова процедуры закрытия файла, и все действия, выполняемые над файловой переменной, выполняются над указанным файлом.

Если при вызове процедуры AssignFile указать пустую строку в качестве имени файла, файловая переменная будет ассоциироваться со стандартными текстовыми файлами – Input при использовании команды Reset и Output при использовании команды Rewrite.

После привязки файловой переменной к внешнему файлу файловая переменная должна быть открыта для того, чтобы было возможно чтение из файла или запись в файл. Для открытия файла используются процедуры Reset, Rewrite и Append.
procedure Reset(var F: file);
procedure Rewrite(var F: file);
procedure Append(var F: TextFile);

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

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

Процедура Append подготавливает существующий текстовый файл для добавления данных. Если файл с именем, ассоциированным с файловой переменной, не существует, возникает ошибка. Указатель позиции для записи данных устанавливается на конец файла. Если файл уже открыт, он закрывается и открывается снова.

После открытия файла можно производить чтение данных из файла и запись данных в файл с помощью процедур Read, Readln, Write и Writeln. Однако процедуры должны соответствовать режиму, в котором был открыт файл. Попытка прочитать данные из файла, открытого только для записи, и попытка записать данные в файл, открытый только для чтения, приведут к возникновению ошибки.

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

Когда программа завершает работу с файлом, файл должен быть закрыт с использованием стандартной процедуры CloseFile.
procedure CloseFile(var f: file);

После закрытия файловой переменной ассоциированный с ней файл модифицируется*. Файловая переменная может быть ассоциирована с другим внешним файлом.

Существуют ещё две полезные функции работы с файлами – Eof, которая определяет, достигнут ли конец файла, и Eoln, которая определяет, достигнут ли конец строки.
function Eof(var f: file): boolean;
function Eoln(var f: text): boolean;

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

Функция Eoln возвращает значение истина, если указатель находится перед символом конца строки, или если достигнут конец файла, т.е. значение функции Eof истинно, и значение ложь в противном случае.

4. Обработка ошибок работы с файлами

По умолчанию все вызовы стандартных процедур ввода/вывода проверяются на ошибки времени выполнения. В случае обнаружения ошибки возбуждается соответствующее исключение, или прерывается работа программы, если возбуждение исключения невозможно.

В Borland Developer Studion автоматическая обработка ошибок может быть включена и выключена с помощью директив компилятора {$I+} и {$I-}. Когда обработка ошибок отключена исключения не возбуждаются, для проверки результатов процедур ввода/вывода необходимо использовать функцию IOResult.
function IOResult(): integer;

Функция IOResult возвращает значение 0, если последняя операции ввода/вывода была успешной. В противном случае функция IOResult возвращает код ошибки. Также функция IOResult сбрасывает флаг ошибки. Если флаг ошибки после неудачной операции ввода/вывода не будет сброшен, все последующие операции ввода/вывода будут заканчиваться неудачей из-за сохранившегося значения флага ошибки.

В редакторе PascalABC, к сожалению, нет функции IOResult. Для проверки можно использовать функцию FileExists, которая проверяет существование файла.
function FileExists(name: string): boolean;

Т.к. файл, открываемый для записи, должен существовать, можно перед использованием операции Reset проверить существование нужного файла. Процедура Rewrite создаёт файл в случае его отсутствия, поэтому проверять наличие файла перед её использованием нет смысла.

5. Использование параметров программы

Если указать в тексте программы имена файлов для ввода и вывода, то каждый раз придётся использовать одни и те же файлы и менять/копировать их содержимое или менять имена файлов в тексте программы. Оба варианта не удобны, и для того, чтобы избежать этого, можно использовать так называемые параметры программы. Эти параметры передаются в программу при запуске.

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

Для работы с параметрами программы в языке Паскаль существуют две стандартные функции – ParamCount и ParamStr.
function ParamCount(): integer;
function ParamStr(index: integer): string;

Функция ParamCount возвращает количество параметров, переданных в программу. Функция ParamStr возвращает параметр с указанным индексом. Пользовательские параметры нумеруются с 1. Если индекс больше числа параметров, возвращается пустая строка.

6. Примеры

6.1. Ввод и вывод двух чисел

program TextFiles; var f: TextFile; { Объявляем файловую переменную } m, n: integer; begin if ParamCount < 2 then { Проверяем количество параметров } writeln('Недостаточно параметров!'); else begin if not FileExists(ParamStr(1)) then { Проверяем существование файла } writeln('Невозможно открыть файл ''', ParamStr(1), ''' для чтения'); else begin AssignFile(f, ParamStr(1)); { Открываем файл } Reset(f); readln(f, m); { Чтение. Первый параметр – файловая переменная } readln(f, n); CloseFile(f); { Закрываем файл } AssignFile(f, ParamStr(2)); { Связываем файловую переменную с другим файлом } Rewrite(f); { Открываем файл для записи } writeln(f, 'm = ', m); { Запись. Первый параметр – файловая переменная } writeln(f, 'n = ', n); CloseFile(f); { Закрываем файл } end; end; end.

6.2. Вывод массива

program TextFiles; const nmax = 10; type mas = array [1..nmax] of real; var f: TextFile; n, i: integer; a: mas; begin ... AssignFile(f, ParamStr(2)); Rewrite(f); writeln(f, 'Массив A из ', n, ' элементов/-а'); for i : = 1 to n do write(f, a[i]:8:2); writeln(f); CloseFile(f); end.

6.3. Ввод массива до конца файла

program TextFiles; const nmax = 10; type mas = array [1..nmax] of real; var f: TextFile; n: integer; a: mas; begin if ParamCount < 2 then { Проверяем количество параметров } writeln('Недостаточно параметров!'); else begin if not FileExists(ParamStr(1)) then { Проверяем существование файла } writeln('Невозможно открыть файл ''', ParamStr(1), ''' для чтения'); else begin AssignFile(f, ParamStr(1)); { Открываем файл } Reset(f); n : = 0; { Ни одного элемента ещё не введено } while not eof(f) do { Организуем цикл до конца файла } begin n : = n + 1; read(f, x[n]); { Вводим элементы массива и считаем их количество } end; CloseFile(f); end; end; end.

6.4. Ввод матрицы с использованием функций Eof и Eoln

program TextFiles; const nmax = 10; type matrix = array [1..nmax, 1..nmax] of real; var f: TextFile; m, n: integer; a: matrix; begin if ParamCount < 2 then { Проверяем количество параметров } writeln('Недостаточно параметров!'); else begin if not FileExists(ParamStr(1)) then { Проверяем существование файла } writeln('Невозможно открыть файл ''', ParamStr(1), ''' для чтения'); else begin AssignFile(f, ParamStr(1)); { Открываем файл } Reset(f); m : = 0; while not eof(f) do { Цикл до конца файла } begin m : = m + 1; n : = 0; while not eoln(f) do { Цикл до конца строки } begin n : = n + 1; read(f, a[m, n]); end; readln(f); { Пропускаем символ конца строки } end; CloseFile(f); end; end; end.


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