Files & Streams

By default ({$I+}) I/O errors raise EInOutError (with SysUtils) or a run-time error.

20.1 File kinds

TextFile (or Text) lines of characters; read/write with Readln/Writeln file of TRecord fixed-size binary records; random access raw bytes in blocks: BlockRead/BlockWrite TFileStream, TMemoryStream,...

object-oriented binary I/O (unit Classes)

20.2 Text files

AssignFile(F, Name) bind a file variable to a name (Assign in TP/ISO modes) open existing file for reading (typed/untyped: read/write) create or truncate open a text file for appending flush and close (Close in TP mode) Eof(F), Eoln(F) end of file / end of line write buffered data SetTextBuf(F, Buf) larger buffer for faster text I/O

program TextFiles;
{$mode objfpc}{$H+}
var
  F: TextFile;
  Line: string;
  I: Integer;
begin
  AssignFile(F, 'notes.txt');    // associate the variable with a file name
  Rewrite(F);                    // create/truncate for writing
  for I := 1 to 3 do
    Writeln(F, 'Line ', I);
  CloseFile(F);

  Append(F);                     // open for appending
  Writeln(F, 'Appended line');
  CloseFile(F);

  Reset(F);                      // open for reading
  while not Eof(F) do
  begin
    Readln(F, Line);
    Writeln(Line);
  end;
  CloseFile(F);
end.

20.3 I/O error handling with IOResult

By default ({$I+}) I/O errors raise EInOutError (with SysUtils) or a run-time error. With {$I-} you check IOResult yourself:

IOResult resets to 0 after it is read — store it in a variable if you need it twice. Modern code usually checks FileExists first and uses exceptions.

AssignFile(F, 'missing.txt');
{$I-}
Reset(F);
{$I+}
if IOResult <> 0 then
  Writeln('Cannot open file')
else
begin
  // ... read ...
  CloseFile(F);
end;

20.4 Typed files (random access)

FilePos(F) returns the current record number; Truncate(F) cuts the file at the current position.

program TypedFiles;
{$mode objfpc}{$H+}
type
  TProduct = record
    Id: Integer;
    Name: string[30];          // ShortString: typed files cannot contain managed types
    Price: Double;
  end;
var
  F: file of TProduct;
  P: TProduct;
  I: Integer;
begin
  AssignFile(F, 'products.dat');
  Rewrite(F);
  for I := 1 to 3 do
  begin
    P.Id := I;
    P.Name := 'Product ' + Chr(Ord('A') + I - 1);
    P.Price := I * 9.99;
    Write(F, P);
  end;

  Writeln('Records: ', FileSize(F));   // 3
  Seek(F, 1);                          // record numbers are 0-based
  Read(F, P);
  Writeln(P.Id, ' ', P.Name, ' ', P.Price:0:2);   // 2 Product B 19.98

  Seek(F, 1);                          // update record 1 in place
  P.Price := 15.00;
  Write(F, P);
  CloseFile(F);
end.

20.5 Untyped files: copying a file

Always pass the record size (1) to Reset/Rewrite for untyped files; the default is 128 bytes.

procedure CopyFileBlocks(const SrcName, DstName: string);
var
  Src, Dst: file;
  Buf: array[0..65535] of Byte;
  NRead, NWritten: Integer;
begin
  AssignFile(Src, SrcName);
  Reset(Src, 1);                 // record size 1 byte
  try
    AssignFile(Dst, DstName);
    Rewrite(Dst, 1);
    try
      repeat
        BlockRead(Src, Buf, SizeOf(Buf), NRead);
        BlockWrite(Dst, Buf, NRead, NWritten);
      until (NRead = 0) or (NWritten <> NRead);
    finally
      CloseFile(Dst);
    end;
  finally
    CloseFile(Src);
  end;
end;

20.6 Streams

abstract base: Read, Write, ReadBuffer, WriteBuffer, Seek, Position, Size, CopyFrom in-memory buffer; LoadFromFile, SaveToFile stream over a string read embedded resources buffered file access (Delphi 10.1+; FPC's BufStream unit offers TReadBufStream/TWriteBufStream) TStreamReader / TStreamWriter text over streams with encodings (Delphi System.Classes; in FPC use TStringList or text files)

program Streams;
{$mode objfpc}{$H+}
uses Classes, SysUtils;
var
  FS: TFileStream;
  N: Integer;
  X: Double;
begin
  FS := TFileStream.Create('data.bin', fmCreate);
  try
    N := 12345;
    X := 2.5;
    FS.WriteBuffer(N, SizeOf(N));
    FS.WriteBuffer(X, SizeOf(X));
  finally
    FS.Free;
  end;

  FS := TFileStream.Create('data.bin', fmOpenRead or fmShareDenyWrite);
  try
    FS.ReadBuffer(N, SizeOf(N));
    FS.ReadBuffer(X, SizeOf(X));
    Writeln(N, ' ', X:0:1, ' size=', FS.Size);   // 12345 2.5 size=12
  finally
    FS.Free;
  end;
end.

20.7 TStringList as a text file tool

For real INI files use TIniFile/TMemIniFile (unit IniFiles).

uses Classes, SysUtils;
var
  SL: TStringList;
begin
  SL := TStringList.Create;
  try
    if FileExists('config.ini') then
      SL.LoadFromFile('config.ini');
    SL.Values['theme'] := 'dark';        // key=value lines
    SL.Add('# saved ' + DateTimeToStr(Now));
    SL.SaveToFile('config.ini');
  finally
    SL.Free;
  end;
end.

20.8 Files and directories

Routine (SysUtils) FileExists, DirectoryExists DeleteFile, RenameFile delete, rename

CreateDir, ForceDirectories, RemoveDir GetCurrentDir, SetCurrentDir

ExtractFileName, ExtractFilePath, ExtractFileExt, ChangeFileExt

IncludeTrailingPathDelimiter, PathDelim, ConcatPaths (FPC)

GetTempDir, GetAppConfigDir (FPC), GetHomePath (Delphi) FileAge, FileSetDate, FileGetAttr

Delphi's System.IOUtils offers record-style APIs: TFile.ReadAllText, TFile.WriteAllLines, TDirectory.GetFiles(Path, '*.pas', TSearchOption.soAllDirectories), TPath.Combine. Lazarus provides the FileUtil and LazFileUtils units (FindAllFiles, CopyFile, DeleteDirectory).

uses SysUtils;
var
  SR: TSearchRec;
begin
  if FindFirst('*.pas', faAnyFile, SR) = 0 then
  try
    repeat
      if (SR.Attr and faDirectory) = 0 then
        Writeln(SR.Name:30, SR.Size:10);
    until FindNext(SR) <> 0;
  finally
    FindClose(SR);
  end;
end.