Файлы
Цель лекции
Научится работать с различными видами файлов.
Файлы
То есть, файл - это часть ПЗУ - Постоянного Запоминающего Устройства, которым может быть жесткий диск, дискета, флэш-карта, CD или DVD. В операционных системах существуют различные файловые структуры, или, как еще говорят, таблицы размещения файлов. Например, в Windows это может быть FAT32 или NTFS. В этих таблицах имена файлов соотносятся с реальным адресом на диске. На уровне машинных кодов, реальный адрес файла представляет собой целое число - номер байта, с которого начинается файл. Также в таблице содержится общее количество байтов, занятых файлом. Но при программировании на языках высокого уровня, таких, как Объектный Паскаль, мы можем пользоваться символическими именами файлов, которые автоматически преобразуются операционной системой в реальные адреса и размеры этих файлов.
Файлы бывают трех типов:
- Текстовые файлы. Это файлы, предназначенные для работы с текстом.
- Типизированные файлы. Такие файлы могут хранить массив записей какого-то одного, указанного типа. Это может быть очень удобно для создания файла с данными.
- Нетипизированные файлы. Это файлы для побайтовой работы с данными любого типа. Могут использоваться для копирования или переноса файлов, тем более что отсутствие типа делает такие файлы более универсальными, совместимыми с любым существующим типом. Кроме того, у нетипизированных файлов обмен данными между памятью и диском более скоростной, что важно при обработке файлов большого размера.
Чтобы открыть доступ к файлу, требуется создать файловую переменную одним из трех способов:
var f1: TextFile; //текстовый файл f2: File of <тип>; //типизированный файл f3: File; //нетипизированный файл
Типизированный файл может работать с любыми типами Объектного Паскаля, кроме файлов. Это может быть целое или вещественное число, символ, строка или даже пользовательский тип данных, например, запись (о записях - в следующей лекции). Например, объявить типизированный файл, работающий с символами, можно так:
var MyF: File of Char; //типизированный символьный файл
Однако для работы с файлами мало объявить переменную файлового типа, нужно еще эту переменную "привязать" к конкретному файлу. Делается это с помощью процедуры AssignFile, в которую передается два параметра - имя файловой переменной, к которой "привязывается" файл, и имя существующего файла, с которым мы хотим работать. Если имя файла указано без адреса, то процедура подразумевает, что файл находится в текущей папке. Если файла нет, программа вызовет ошибку, поэтому рекомендуется всегда перед связыванием файловой переменной с файлом вначале проверить - существует ли файл? Делается это с помощью функции FileExists, с которой нам уже доводилось работать, и которая возвращает True, если указанный файл существует, и False в противном случае. Вот пример связывания файловой переменной с файлом:
var f1: TextFile; //текстовый файл begin if FileExists('c:\01\myfile.txt') then begin AssignFile(f1, 'c:\01\myfile.txt'); //связали файловую переменную с файлом ... //дальнейшая работа с файлом end; //if
После того, как мы связали файловую переменную с файлом, мы можем обращаться к ней, как к файлу. Мы можем открыть её, считать или записать информацию, закрыть. А после работы с файлом его обязательно нужно закрыть! Делается это процедурой CloseFile, в которую передается всего один параметр - имя файловой переменной, например:
CloseFile(f1); //закрыли файл, с которым была связана файловая переменная f1
Во время работы с файлом может возникнуть исключительная ситуация. Внимание! Такой термин мы встречаем впервые, и он очень важен для понимания. Что такое исключительная ситуация? Это любая ошибка программы, которая может произойти во время работы. Например, вы открыли какой-то сетевой файл и начали работу с ним. А другой пользователь в это время взял, да удалил этот файл. Или вы пытаетесь работать с файлом, который содержит вирус. Как только вы обратились к нему, ваш антивирус вмешивается, и удаляет файл, или переносит его в карантин. Или же ваш файл "битый" - содержит ошибку, и не может прочитаться.
При попытке чтения из несуществующего файла, или записи в него, или при работе с "битым" файлом произойдет ошибка и возникнет исключительная ситуация. Если вы не обработаете эту ошибку, то ваша программа, скорее всего, намертво повиснет. Вам придется вызывать Диспетчер задач Windows, и снимать задачу - принудительно закрывать вашу программу. Поэтому в любой ситуации, когда имеется риск возникновения исключительной ситуации, программист ВСЕГДА должен ее обработать. Для этого существует блок try-finally-end:
try //блок кода, в котором может произойти ошибка finally //код, который должен выполниться в любом случае, например, код закрытия файла end;
Между служебными словами try-finally вы вписываете потенциально опасный код. То есть, код, который может вызвать ошибку. А между finally-end указываете тот код, который должен быть выполнен в любом случае, даже при возникновении ошибки. Вот более правильный пример того, как нужно связывать файл с переменной:
var f1: TextFile; //текстовый файл begin try AssignFile(f1, 'c:\01\myfile.txt'); //связали файловую переменную с файлом …; //дальнейшая работа с файлом - чтение/запись finally CloseFile(f1); //по окончании закрываем файл end; //try
Как видите, здесь попытку связать файловую переменную с файлом, и дальнейшую работу с ним мы заключили в блок try-finally. Поскольку блок обрабатывает исключительные ситуации, можно не делать дополнительной проверки на существование файла. Получилась ли открытие файла, и работа с ним, или произошла ошибка, в любом случае будет выполнено закрытие файла, что убережет программу от краха.
Есть и другой способ обработки исключительных ситуаций - блок try-except-end:
try //блок кода, в котором может произойти ошибка except //код, который выполнится в том случае, если в блоке между try-except произошла ошибка end;
Пример работы с файлом можно представить и так:
var f1: TextFile; //текстовый файл begin try AssignFile(f1, 'c:\01\myfile.txt'); //связали файловую переменную с файлом …; //дальнейшая работа с файлом - чтение/запись CloseFile(f1); //по окончании закрываем файл except ShowMessage('Внимание! Произошла ошибка открытия файла.'); end; //try
Каким образом обрабатывать исключительные ситуации - решать вам. В зависимости от ситуации, иногда бывает удобней первый способ, иногда - второй.
После связывания файловой переменной с файлом, этот файл нужно инициировать, то есть, указать направление передачи данных - из файла (чтение), в файл (запись), и в обоих направлениях. Если мы открываем файл для чтения, нам следует воспользоваться стандартной процедурой Reset:
Reset(f1);
При этом указатель устанавливается в начало файла, в нулевую позицию.
Если нам нужно создать новый файл, или перезаписать существующий, используется процедура Rewrite:
Rewrite(f2);
Если файл уже существовал, он будет перезаписан без каких либо предупреждений. Если же нам нужно открыть для записи существующий файл, не удаляя содержащейся в нем информации, воспользуемся процедурой Append:
Append(f1);
Файл будет открыт для записи, а указатель переместится в конец файла. Однако, процедура Append применима только к текстовым файлам, то есть к переменным типа TextFile.
Во время работы с файлами можно использовать функции Bof и Eof - первая возвращает истину, если указатель находится в начале файла, вторая - если в конце. Обе функции имеют параметр - файловую переменную:
if Eof(f1) then ShowMessage('Достигнут конец файла.');
Есть еще один способ проверки существования файла, и вообще обработки ошибок ввода-вывода в файл. Это - функция IOResult, которая возвращает ноль при отсутствии ошибок ввода-вывода, и номер ошибки в противном случае. Но использовать эту функцию просто так не получится. Lazarus автоматически обрабатывает ошибки ввода-вывода. Прежде чем использовать IOResult, нужно дать процессору команду отключить автоматическую обработку этих ошибок. Для этого существует директива процессора {$I-}. Затем мы выполняем опасный участок кода, где может произойти ошибка, после чего снова включаем автоконтроль операций ввода-вывода директивой {$I+}. Затем вызываем IOResult. Если операция произошла успешно, то функция вернет ноль. Пример:
var f1: TextFile; begin AssignFile(f1, 'MyText.txt'); //связали файл с переменной {$I-} //отключили автоконтроль ввода-вывода Reset(f1); //пытаемся открыть файл для чтения {$I+} //снова включили автоконтроль ввода-вывода if IOResult <> 0 then ShowMessage('Внимание! Произошла ошибка открытия файла.');