Копия викизеркало

Это зеркало вики с pascal.chat. Правка, история и вход — там.

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(http://www.freepascal.org/docs-html/rtl/system/ioresult.html)).

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

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

pascal
[???????????? ???? ???????????? 'System'](<http://www.freepascal.org/docs-html/rtl/system/index-5.html>).

Пример

Пример работы с текстовым файлом (тип 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 the Internet Archive kept it on 2023-03-26, last changed on 2020-02-15. 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.

Версия 3, последнее изменение: WikiArchive, .