Sign in Join
Wiki copytemporary

File Handling In Pascal/ru

English · Español · Français · Suomi · Русский · العربية · 中文(中国) · 中文(台灣) · 日本語

Введение

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

Процедурный стиль

Это довольно старый стиль, использующейся ещё во времена, когда Pascal не был объектно-ориентированным языком. Суть его в том, что задается тип файла, определяющий, какие будут храниться в нем данные. Для этого, используется конструкция вида: file of <тип данных>, где <тип данных> - название типа, который хранит в себе файл. Помимо стандартных типов (integer, extended, char и т.д.), существует особый тип - TextFile. Он определят, что каждая строка заканчивается специальным(ми) символом(ами) конца строки (См. LineEnding). Эти файлы могут быть открыты и отредактированы внутри среды Lazarus или в любом другом текстовом редакторе.

Ниже представлены примеры создания собственных типов файлов:

pascal
...
type
  TIntegerFile  = file of integer;  // Позволяет писать только целые числа в файл
  TExtendedFile = file of extended; // Позволяет писать только дробные цифры в файл
  TCharFile     = file of char;     // Позволяет писать только одиночные символы в файл

Обработка ошибок ввода/вывода

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

Это задаётся с помощью специальной директивы компилятора:

pascal
{$I+} // В случаи ошибки будет вызвано исключение EInOutError (по умолчанию)
{$I-} // Подавлять ошибки ввода-вывода: проверьте переменную IOResult для получения кода ошибки.

В случаи подавления ошибок ввода-вывода ({$I-}) результат операции с файлом будет храниться в переменной IOResult типа cardinal (числовой тип). Каждое число, хранимое в IOResult определяет тип возникшей ошибки(подробнее: 1).

Процедуры работы с файлами

Эти процедуры и функции находятся в модуле system. Для более подробной информации смотрите документацию FPC:

ссылка на модуль 'System'.

Пример

Пример работы с текстовым файлом (тип TextFile):

pascal
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 после каждой операции с файлами для контроля ошибок.

Ниже приведен пример записи текста в конец файла:

pascal
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.

Чтение текстового файла:

pascal
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.

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.

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

pascal
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 байт.

pascal
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):

pascal
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 обратно в файл:

pascal
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.

Демо: сохранить одну строку в файл

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

pascal
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.

Смотрите так же


From the Free Pascal wiki, page "File Handling In Pascal/ru", as it stood on 2020-02-14 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 1, last changed by WikiArchive .

Edited on wiki.freepascal.org by 2 people, 2016–2020

From the official wiki's backup of 26 May 2024: who edited this page there, and when. Edits made there after that date were not recorded, and the earlier versions are not shown here; the main page says why.

WhenWhoWhat changed
AmberCreated page
AmberОбработка ошибок ввода/вывода
AmberПроцедурный стиль
TrevFixed syntax highlighting; deleted category included in page template (minor)

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.