File Handling In Pascal/ru
│
العربية (ar) │
English (en) │
español (es) │
suomi (fi) │
français (fr) │
日本語 (ja) │
русский (ru) │
中文(中国大陆) (zh_CN) │
中文(臺灣) (zh_TW) │
Введение
Каждый программист должен знать, как работать с файлами. Файлы используются для сохранения данных, т. е. в качестве хранилища данных, таким образом, что они могут быть получены в любой момент, без их воссоздания. Файлы можно использовать для сохранения пользовательских настроек, журналов ошибок, измерение или вычисление результатов, и многое другое. В данном разделе описываются основы использования файлов.
Процедурный стиль
Это довольно старый стиль, использующейся ещё во времена, когда Pascal не был объектно-ориентированным языком. Суть его в том, что задается тип файла, определяющий, какие будут храниться в нем данные. Для этого, используется конструкция вида: file of <тип данных>, где <тип данных> - название типа, который хранит в себе файл. Помимо стандартных типов (integer, extended, char и т.д.), существует особый тип - TextFile. Он определят, что каждая строка заканчивается специальным(ми) символом(ами) конца строки (См. LineEnding). Эти файлы могут быть открыты и отредактированы внутри среды Lazarus или в любом другом текстовом редакторе.
Ниже представлены примеры создания собственных типов файлов:
...
type
TIntegerFile = file of integer; // Позволяет писать только целые числа в файл
TExtendedFile = file of extended; // Позволяет писать только дробные цифры в файл
TCharFile = file of char; // Позволяет писать только одиночные символы в файл
Обработка ошибок ввода/вывода
Параметр компилятора обработки ошибок ввода/вывода указывает, как должна вести себя программа при возникновении ошибки в процессе работы с файлами: вызвать исключение или хранить результат операции ввода/вывода в специальной переменной IOResult.
Это задаётся с помощью специальной директивы компилятора:
{$I+} // В случаи ошибки будет вызвано исключение EInOutError (по умолчанию)
{$I-} // Подавлять ошибки ввода-вывода: проверьте переменную IOResult для получения кода ошибки.
В случаи подавления ошибок ввода-вывода ({$I-}) результат операции с файлом будет храниться в переменной IOResult типа cardinal (числовой тип). Каждое число, хранимое в IOResult определяет тип возникшей ошибки(подробнее: [1]).
Процедуры работы с файлами
Эти процедуры и функции находятся в модуле system. Для более подробной информации смотрите документацию FPC:
ссылка на модуль 'System'.
- AssignFile (не допускайте использование процедуры Assign) - Связывает переменную с файлом
- Append - Открывает существующий файл для записи данных в конец и их редактирования
- BlockRead - Чтение данных из не типизированного файла в память
- BlockWrite - Запись данных из памяти в не типизированный файл
- CloseFile (не допускайте использование процедуры Close) - Закрыть открытый файл
- EOF - Проверка наличия конца файла
- Erase - Стереть файл с диска
- FilePos - Получить позицию в файле
- FileSize - Получить размер файла
- Flush - Записать файловый буфер на диск
- IOResult - Возвращает результат последней операции ввода\вывода
- Read - Считать из текстового файла
- ReadLn - Считать из текстового файла и перейти к следующей строке
- Reset - Открыть файл для чтения
- Rewrite - Создать и открыть файл для записи
- Seek - Изменить позицию в файле
- SeekEOF - Переместить позицию в файле в его конец
- SeekEOLn - Переместить позицию в файле в конец строки
- Truncate - Удалить все данные, после текущей позиции
- Write - Записать переменную в файл
- WriteLn - Записать переменную в текстовый файл и перейти к новой строке
Пример
Пример работы с текстовым файлом (тип TextFile):
program CreateFile;
uses
Sysutils;
const
C_FNAME = 'textfile.txt';
var
tfOut: TextFile;
begin
// Связываем имя файла с переменной
AssignFile(tfOut, C_FNAME);
// Использовать исключение для перехвата ошибок (это по умолчанию и указывать не обязательно)
{$I+}
// Для обработки исключений, используем блок try/except
try
// Создать файл, записать текст и закрыть его.
rewrite(tfOut);
writeln(tfOut, 'Пример текстового файла!');
writeln(tfOut, 'Пример записи числа: ', 42);
CloseFile(tfOut);
except
// Если ошибка - отобразить её
on E: EInOutError do
writeln('Ошибка обработки файла. Детали: ', E.ClassName, '/', E.Message);
end;
// Выводим результат операции и ожидаем нажатие Enter
writeln('Файл ', C_FNAME, ' создан. Нажмите ВВОД для выхода.');
readln;
end.
Теперь откройте файл в любом текстовом редакторе и вы увидите, что пример текста, записан в нем! Вы можете проверить работу обработки ошибок, установив атрибут файла "только для чтения" и снова запустив программу.
Обратите внимание, что в примере используется блок try/except. Данный способ позволяет выполнять несколько операций с файлами и использовать обработку исключений. Вы также можете использовать режим {$I-}, но тогда вам придется проверять переменную IOResult после каждой операции с файлами для контроля ошибок.
Ниже приведен пример записи текста в конец файла:
program AppendToFile;
uses
Sysutils;
const
C_FNAME = 'textfile.txt';
var
tfOut: TextFile;
begin
// Связываем имя файла с переменной
AssignFile(tfOut, C_FNAME);
// Для обработки исключений, используем блок try/except
try
// Открыть файл для записи в конец, записать текст и закрыть его.
append(tfOut);
writeln(tfOut, 'Ещё пример текстового файла!');
writeln(tfOut, 'Результат 6 * 7 = ', 6 * 7);
CloseFile(tfOut);
except
on E: EInOutError do
writeln('Ошибка обработки файла. Детали: ', E.Message);
end;
// Выводим результат операции и ожидаем нажатие Enter
writeln('Файл ', C_FNAME, ' возможно содержит больше текста. Нажмите ВВОД для выхода.');
readln;
end.
Чтение текстового файла:
program ReadFile;
uses
Sysutils;
const
C_FNAME = 'textfile.txt';
var
tfIn: TextFile;
s: string;
begin
// Вывод некой информации
writeln('Чтение содержимого файла: ', C_FNAME);
writeln('=========================================');
// Связываем имя файла с переменной
AssignFile(tfIn, C_FNAME);
// Для обработки исключений, используем блок try/except
try
// Открыть файл для чтения
reset(tfIn);
// Считываем строки, пока не закончится файл
while not eof(tfIn) do
begin
readln(tfIn, s);
writeln(s);
end;
// Готово. Закрываем файл.
CloseFile(tfIn);
except
on E: EInOutError do
writeln('Ошибка обработки файла. Детали: ', E.Message);
end;
// Выводим результат операции и ожидаем нажатие Enter
writeln('=========================================');
writeln('Файл ', C_FNAME, ' считан. Нажмите ВВОД для выхода.');
readln;
end.
Объектный стиль
В дополнение к старому методу обработки файлов, упомянутому выше процедурному стилю, существует новая система, которая использует концепции потоков (данных) на более высоком уровне абстракции. Это означает, что данные могут считываться или записываться в любом месте (диск, память, аппаратные порты и т. д.) через один универсальный интерфейс.
Кроме того, большинство классов обработки строк, могут иметь возможность загружать/сохранять содержимое из/в файл. Эти методы обычно называются SaveToFile и LoadFromFile.
Двоичные файлы
Для прямого доступа к файлам, так же удобно использовать класс TFileStream. Этот класс представляет собой инкапсуляцию системных процедур FileOpen, FileCreate, FileRead, FileWrite, FileSeek и FileClose, расположенных в модуле SysUtils.
В приведенном ниже примере обратите внимание, что обработка действий с файлом, расположена внутри блока try..except. Поэтому обработка ошибок происходит так же, как в случаи использования процедур работы с файлами в классическом Pascal.
program WriteBinaryData;
{$mode objfpc}
uses
Classes, Sysutils;
const
C_FNAME = 'binarydata.bin';
var
fsOut : TFileStream;
ChrBuffer: array[0..2] of char;
begin
// Создать некоторые случайные данные, которые будут храниться в файле
ChrBuffer[0] := 'A';
ChrBuffer[1] := 'B';
ChrBuffer[2] := 'C';
// Перехват ошибок в случае, если файл не может быть создан
try
// Создать экземпляр потока файла, записать в него данные и уничтожить его, чтобы предотвратить утечки памяти
fsOut := TFileStream.Create( C_FNAME, fmCreate);
fsOut.Write(ChrBuffer, sizeof(ChrBuffer));
fsOut.Free;
// Обработка ошибки
except
on E:Exception do
writeln('Файл ', C_FNAME, ' не создан, так как: ', E.Message);
end;
// Выводим результат операции
writeln('Файл ', C_FNAME, ' создан. Нажмите ВВОД для выхода.');
//Ожидаем нажатие Enter
readln;
end.
Вы так же можете загрузить весь файл в память, если его размер существенно меньше, чем имеющийся системной памяти. Если файл превышает размер системной памяти, то ваша операционная система начнет использовать файл подкачки, снижая скорость работы с файлом.
program ReadBinaryDataInMemoryForAppend;
{$mode objfpc}
uses
Classes, Sysutils;
const
C_FNAME = 'binarydata.bin';
var
msApp: TMemoryStream;
begin
// Создаем поток в памяти
msApp := TMemoryStream.Create;
// Перехват ошибок в случае их возникновения
try
// Считать данные в память
msApp.LoadFromFile(C_FNAME);
// Переходим в конец данных
msApp.Seek(0, soEnd);
// Запись неких данных в поток
msApp.WriteByte(68);
msApp.WriteAnsiString('Некий текст');
msApp.WriteDWord(671202);
// Запись данных на диск (файл будет перезаписан)
msApp.SaveToFile(C_FNAME);
// Обработка ошибки
except
on E:Exception do
writeln('Файл ', C_FNAME, ' не удалось считать или записать, так как: ', E.Message);
end;
// Освобождаем память и уничтожаем объект
msApp.Free;
// Выводим результат операции и ожидаем нажатие Enter
writeln('Файл ', C_FNAME, ' дописан. Нажмите ВВОД для выхода.');
readln;
end.
Для работы с файлами большого объёма, рекомендуется использовать буфер, например в 4096 байт.
var
TotalBytesRead, BytesRead : Int64;
Buffer : array [0..4095] of byte; // или, array [0..4095] of char
FileStream : TFileStream;
try
FileStream := TFileStream.Create;
FileStream.Position := 0; // Установим позицию в начало файла
while TotalBytesRead <= FileStream.Size do // Пока объем считанных данных меньше размера файла
begin
BytesRead := FileStream.Read(Buffer,sizeof(Buffer)); // Считать 4096 байт данных
inc(TotalBytesRead, BytesRead); // Увеличиваем TotalByteRead на размер буфера, т.е. 4096 байт
// Что-то делаем с буфером данных
end;
Копирование файла
Теперь,зная методы работы с файлами, мы можем реализовать простую функцию копирования файла, скажем FileCopy.(такой функции нет в FreePascal, хотя Lazarus её имеет copyfile):
program FileCopyDemo;
// Пример функции FileCopy
{$mode objfpc}
uses
classes;
const
fSource = 'test.txt';
fTarget = 'test.bak';
function FileCopy(Source, Target: string): boolean;
// Копируем файл с путем в Source в файл с путем Target.
// Кэшируем весь файл в память.
// В случаи успеха возвращаем true, в случаи ошибки - false.
var
MemBuffer: TMemoryStream;
begin
result := false;
MemBuffer := TMemoryStream.Create;
try
MemBuffer.LoadFromFile(Source);
MemBuffer.SaveToFile(Target);
result := true
except
//Подавляем исключение; результатом функции является значение false по умолчанию
end;
// Очистка
MemBuffer.Free
end;
// Пример использования
begin
If FileCopy(fSource, fTarget)
then writeln('Файл ', fSource, ' скопирован в ', ftarget)
else writeln('Файл ', fSource, ' не скопирован в ', ftarget);
readln()
end.
Обработка текстовых файлов (TStringList)
Для текстовых файлов можно использовать класс TStringList, чтобы загрузить весь файл в память и иметь легкий доступ к его строкам. Вы также можете записать StringList обратно в файл:
program StringListDemo;
{$mode objfpc}
uses
Classes, SysUtils;
const
C_FNAME = 'textfile.txt';
var
slInfo: TStringList;
begin
// Создаем TStringList для обработки текстового файла
slInfo := TStringList.Create;
// Для обработки исключений, используем блок try/except
try
// Загружаем файл в память
slInfo.LoadFromFile(C_FNAME);
// Добавляем некие строки
slInfo.Add('Некая строка');
slInfo.Add('Ещё одна строка.');
slInfo.Add('Всё, хватит.');
slInfo.Add('Сейчас ' + DateTimeToStr(now));
// Записать содержимое на диск, заменив исходное содержимое
slInfo.SaveToFile(C_FNAME);
except
// Обработка ошибки
on E: EInOutError do
writeln('Произошла ошибка обработки файла. Причина: ', E.Message);
end;
// Очистка
slInfo.Free;
// Выводим результат операции и ожидаем нажатие Enter
writeln('Файл ', C_FNAME, ' обновлен. Нажмите ВВОД для выхода.');
readln;
end.
Демо: сохранить одну строку в файл
Для того, чтобы сохранить одну строку в файл, вы можете воспользоваться процедурой, описанной ниже.
program SaveStringToPathDemo;
{$mode objfpc}
uses
Classes, sysutils;
const
C_FNAME = 'textstringtofile.txt';
// SaveStringToFile: функция для хранения строк текста в файле на диске.
// Если результат функции равен True, то строка была написана
// Иначе произошла ошибка
function SaveStringToFile(theString, filePath: AnsiString): boolean;
var
fsOut: TFileStream;
begin
// По умолчанию результат неудачный
result := false;
// Записать данную строку в файл, перехватывая ошибки в процессе записи.
try
fsOut := TFileStream.Create(filePath, fmCreate);
fsOut.Write(theString[1], length(theString));
fsOut.Free;
// На данном этапе известно, что запись прошла успешно.
result := true
except
on E:Exception do
writeln('Строка не записана. Детали: ', E.ClassName, ': ', E.Message);
end
end;
//
// Основная программа
//
begin
// Пытаемся сохранить текст в файл и выводим результат операции
if SaveStringToFile('>> этот текст сохраняется <<', C_FNAME) then
writeln('Текст успешно записан в файл.')
else
writeln('Не удалось сохранить текст в файл.');
// Ждем нажатия Enter
readln
end.