Difference between revisions of "File Handling In Pascal/es"

From Free Pascal wiki
Jump to navigationJump to search
Line 194: Line 194:
 
   readln;
 
   readln;
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
 +
 +
==Object style==
 +
In addition to the old style file handling routines mentioned above, a new system exists that uses the concept of streams (- of data) at a higher abstraction level. This means data can be read from or written to any location (disk, memory, hardware ports etc.) by one uniform interface.
 +
 +
In addition, most string handling classes have the ability to load and save content from/to a file. These methods are usually named SaveToFile and LoadFromFile. A lot of other objects (such as Lazarus grids) have similar functionality, including Lazarus datasets (DBExport). It pays to look through the documentation or source code before trying to roll your own save/load routines.
 +
 +
===Binary files===
 +
For opening files for direct access TFileStream can be used. This class is an encapsulation of the system procedures FileOpen, FileCreate, FileRead, FileWrite, FileSeek and FileClose which resides in unit SysUtils.
 +
 +
[http://www.freepascal.org/docs-html/rtl/sysutils/ioroutines.html IO routines]
 +
 +
In the example below, note how we encapsulate the file handling action with a try..except block so that errors are handled correctly just as with the classic file handling routines (as not to convolute the example the fsOut.write is not put inside a try...finally block).
 +
<syntaxhighlight>program WriteBinaryData;
 +
{$mode objfpc}
 +
 +
uses
 +
  Classes, Sysutils;
 +
 +
const
 +
  C_FNAME = 'binarydata.bin';
 +
 +
var
 +
  fsOut    : TFileStream;
 +
  ChrBuffer: array[0..2] of char;
 +
 +
begin
 +
  // Set up some random data that will get stored
 +
  ChrBuffer[0] := 'A';
 +
  ChrBuffer[1] := 'B';
 +
  ChrBuffer[2] := 'C';
 +
 +
  // Catch errors in case the file cannot be created
 +
  try
 +
    // Create the file stream instance, write to it and free it to prevent memory leaks
 +
    fsOut := TFileStream.Create( C_FNAME, fmCreate);
 +
    fsOut.Write(ChrBuffer, sizeof(ChrBuffer));
 +
    fsOut.Free;
 +
 +
  // Handle errors
 +
  except
 +
    on E:Exception do
 +
      writeln('File ', C_FNAME, ' could not be created because: ', E.Message);
 +
  end;
 +
 +
  // Give feedback and wait for key press
 +
  writeln('File ', C_FNAME, ' created if all went ok. Press Enter to stop.');
 +
  readln;
 +
end.</syntaxhighlight>
 +
 +
 +
You can load entire files into memory too if its size is comparatively smaller than available system memory. Bigger sizes might work but your operating system will start using the page/swap file, making the exercise useless from a performance standpoint.
 +
 +
<syntaxhighlight>program ReadBinaryDataInMemoryForAppend;
 +
{$mode objfpc}
 +
 +
uses
 +
  Classes, Sysutils;
 +
 +
const
 +
  C_FNAME = 'binarydata.bin';
 +
 +
var
 +
  msApp: TMemoryStream;
 +
 +
begin
 +
  // Set up the stream
 +
  msApp := TMemoryStream.Create;
 +
 +
  // Catch errors in case the file cannot be read or written
 +
  try
 +
    // Read the data into memory
 +
    msApp.LoadFromFile(C_FNAME);
 +
 +
    // Seek the end of the stream so data can be appended
 +
    msApp.Seek(0, soEnd);
 +
 +
    // Write some arbitrary data to the memory stream
 +
    msApp.WriteByte(68);
 +
    msApp.WriteAnsiString('Some extra text');
 +
    msApp.WriteDWord(671202);
 +
 +
    // Store the data back on disk, overwriting the previous contents
 +
    msApp.SaveToFile(C_FNAME);
 +
 +
  // Handle errors
 +
  except
 +
    on E:Exception do
 +
      writeln('File ', C_FNAME, ' could not be read or written because: ', E.Message);
 +
  end;
 +
 +
  // Clean up
 +
  msApp.Free;
 +
 +
  // Give feedback and wait for key press
 +
  writeln('File ', C_FNAME, ' was extended if all went ok. Press Enter to stop.');
 +
  readln;
 +
end.
 +
</syntaxhighlight>
 +
 +
With larger files of many Gb, you may want to read in buffers of, say, 4096 bytes (you're advised to use a multiple of the filesytem cluster or block size) and do something with the data of each buffer read.
 +
 +
<syntaxhighlight>
 +
var
 +
  TotalBytesRead, BytesRead : Int64;
 +
  Buffer : array [0..4095] of byte;  // or, array [0..4095] of char
 +
  FileStream : TFileStream;
 +
 +
try
 +
  FileStream := TFileStream.Create;
 +
  FileStream.Position := 0;  // Ensure you are at the start of the file
 +
  while TotalBytesRead <= FileStream.Size do  // While the amount of data read is less than or equal to the size of the stream do
 +
  begin
 +
    BytesRead := FileStream.Read(Buffer,sizeof(Buffer));  // Read in 4096 of data
 +
    inc(TotalBytesRead, BytesRead);                      // Increase TotalByteRead by the size of the buffer, i.e. 4096 bytes
 +
    // Do something with Buffer data
 +
  end;   
 +
</syntaxhighlight>
 +
 +
=== FileCopy ===
 +
 +
Con lo aprendido anteriormente podemos implementar una función simple de copia de ficheros (FreePascal no tiene ninguna en su RTL aunque Lazarus tiene [[copyfile|copyfile]]) - se puede ajustar para ficheros de mayor tamaño, etc:
 +
<syntaxhighlight>
 +
program FileCopyDemo;
 +
// Demonstración de la función FileCopy.
 +
 +
{$mode objfpc}
 +
 +
uses
 +
  classes;
 +
 +
const
 +
  fSource = 'test.txt';
 +
  fTarget = 'test.bak';
 +
 +
function FileCopy(Source, Target: string): boolean;
 +
// Copia fuente a destino, sobreescribe destino.
 +
// Cachea el contenido completo en memoria.
 +
// Retorna true si tiene exito; falso en caso contrario.urns true if succeeded; false if failed.
 +
var
 +
  MemBuffer: TMemoryStream;
 +
begin
 +
  result := false;
 +
  MemBuffer := TMemoryStream.Create;
 +
  try
 +
    MemBuffer.LoadFromFile(Source);
 +
    MemBuffer.SaveToFile(Target);
 +
    result := true
 +
  except
 +
    //swallow exception; function result is false by default
 +
  end;
 +
  // Clean up
 +
  MemBuffer.Free
 +
end;
 +
 +
begin
 +
  If FileCopy(fSource, fTarget)
 +
    then writeln('File ', fSource, ' copied to ', ftarget)
 +
    else writeln('File ', fSource, ' not copied to ', ftarget);
 +
  readln()
 +
end.
 +
</syntaxhighlight>
 +
 +
===Handling Text files (TStringList)===
 +
In general, for text files you can use the TStringList class to load the entire file into memory and have easy access to its lines. Of course, you can also write the StringList back to a file:
 +
 +
<syntaxhighlight>program StringListDemo;
 +
{$mode objfpc}
 +
 +
uses
 +
  Classes, SysUtils;
 +
 +
const
 +
  C_FNAME = 'textfile.txt';
 +
 +
var
 +
  slInfo: TStringList;
 +
 +
begin
 +
  // Create an instance of the string list to handle the textfile
 +
  slInfo := TStringList.Create;
 +
 +
  // Embed the file handling in a try/except block to handle errors gracefully
 +
  try
 +
    // Load the contents of the textfile completely in memory
 +
    slInfo.LoadFromFile(C_FNAME);
 +
 +
    // Add some more contents
 +
    slInfo.Add('An extra line appended to the text');
 +
    slInfo.Add('And another one.');
 +
    slInfo.Add('Let''s stop here.');
 +
    slInfo.Add('It is now ' + DateTimeToStr(now));
 +
 +
    // And write the contents back to disk, replacing the original contents
 +
    slInfo.SaveToFile(C_FNAME);
 +
 +
  except
 +
    // If there was an error the reason can be found here
 +
    on E: EInOutError do
 +
      writeln('File handling error occurred. Reason: ', E.Message);
 +
  end;
 +
 +
  // Clean up
 +
  slInfo.Free;
 +
 +
  // Give feedback and wait for key press
 +
  writeln('File ', C_FNAME, ' updated if all went ok. Press Enter to stop.');
 +
  readln;
 +
end.</syntaxhighlight>
 +
 +
===Demo: save single string to a file===
 +
In order to write a single string to a stream you might want to use the procedure defined below. Note that strings in FreePascal can be extremely long, this is also a useful way to write a big block of textdata to a file.
 +
<syntaxhighlight>program SaveStringToPathDemo;
 +
{$mode objfpc}
 +
 +
uses
 +
  Classes, sysutils;
 +
 +
const
 +
  C_FNAME = 'textstringtofile.txt';
 +
 +
// SaveStringToFile: function to store a string of text into a diskfile.
 +
//  If the function result equals true, the string was written ok.
 +
//  If not then there was some kind of error.
 +
function SaveStringToFile(theString, filePath: AnsiString): boolean;
 +
var
 +
  fsOut: TFileStream;
 +
begin
 +
  // By default assume the writing will fail.
 +
  result := false;
 +
 +
  // Write the given string to a file, catching potential errors in the process.
 +
  try
 +
    fsOut := TFileStream.Create(filePath, fmCreate);
 +
    fsOut.Write(theString[1], length(theString));
 +
    fsOut.Free;
 +
 +
    // At his point it is known that the writing went ok.
 +
    result := true
 +
 +
  except
 +
    on E:Exception do
 +
      writeln('String could not be written. Details: ', E.ClassName, ': ', E.Message);
 +
  end
 +
end;
 +
 +
//
 +
// Main program
 +
//
 +
begin
 +
  // Try to save a simple textstring in a file and give feedback of sucess.
 +
  if SaveStringToFile('>> this text gets stored <<', C_FNAME) then
 +
    writeln('Text succesfully written to a file')
 +
  else
 +
    writeln('Writing text to a file failed.');
 +
 +
  // Wait for the user to press Enter
 +
  readln
 +
end.
 +
</syntaxhighlight>

Revision as of 09:34, 25 June 2015

العربية (ar) English (en) español (es) suomi (fi) français (fr) 日本語 (ja) русский (ru) 中文(中国大陆)‎ (zh_CN) 中文(台灣)‎ (zh_TW)

Descripción

Algo que necesitan conocer todos los programadores es como trabajar con ficheros. Los ficheros se utilizan para almacenar datos de forma persistente, i.e. almacenar datos de manera que puedan ser retornados en un momento posterior sin tener que volver a crearlos. Los ficheros pueden utilizarse para almacenar configuraciones de usuario, reportes de error, medidas o resultados de cálculos, etc. Esta página explica lo básico sobre manejo de ficheros.

Estilo del procedimiento antiguo

Cuando se utilizan ficheros en el modo clásico de Pascal (no orientado a objetos) se puede utilizar el tipo 'TextFile' (o simplemente 'Text') para almacenar texto, que está estructurado típicamente en líneas. Cada línea finaliza con una marca de fin de línea (EOL=End Of Line). En este tipo de fichero se pueden almacenar tanto cadenas (strings) como números (integer, real...) formateados además de la forma que más nos convenga. Estos ficheros pueden posteriormente abrirse para visualizarlos o editarlos mismamente con el IDE de Lazarus o cualquier otro editor de texto.

Para propósitos específicos se puede crear un tipo de fichero personalizado que puede almacenar únicamente un tipo de dato. Por ejemplo:

...
type
  TIntegerFile  = file of integer;  // Permite escribir únicamete números de tipo entero (integer) al fichero.
  TExtendedFile = file of extended; // Permite escribir úncamente números de tipo real al fichero.
  TCharFile     = file of char;     // Permite escribir únicamente caracteres simples al fichero.

Manejo de errores de Entrada/Salida

El I/O error handling flag o flag (bandera) de Entrada/Salida (I/O -> Input/Output) indica al compilador como manejarse con las situaciones de error: lanzar una excepción o almacenar el resultado de la entrada/salida en la variable IOResult.

El flag de manejo del error de entrada/salida es una directiva del compilador. Para habilitarlo/deshabilitarlo escribimos:

{$I+} // Los errores generarán una excepción EInOutError (por defecto)
{$I-} // Suprime los errores de entrada/salida: chequea la veriable IOResult para saber su código de error.

Mediante la supresión de los errores de entrada/salida utilizando ({$I-}) los resultados de la operación con ficheros se meterán en la variable IOSResult. Este es un cardinal (number) type. Cada uno de los distintos numeros indicarán los diferentes errores obtenidos. Estos códigos de error se pueden consultar en la documentación [1].

Procedimientos de fichero

Estas funciones y procedimientos de manejo de ficheros se encuentran en la unit 'system'. Ver la documentación de FPC para más detalles:

Referenccia sobre la unidad 'System'.

  • AssignFile (previene del uso del antiguo procedimiento Assign ) - Asigna un nombre a un fichero.
  • Append - Abre un fichero ya existente en el modo añadir al final del mismo.
  • BlockRead - Lee un bloque de datos de un fichero sin tipo poniéndolo en memoria.
  • BlockWrite - Escribe un bloque de datos desde la memoria hacia un fichero sin tipo.
  • CloseFile (previene el uso del procedimiento antiguo Close ) - Cierra un fichero que está abierto.
  • EOF - Chequea si se ha llegado al final del fichero (EOF=End Of File), devuelve true si ha llegado y false si todavía no.
  • Erase - Borra un fiechro del disco.
  • FilePos - Retorna da la posición en que nos encontramos dentro del fichero.
  • FileSize - Retorna el tamaño del fichero.
  • Flush - Escribe los buffers de fichero a disco.
  • IOResult - Retorna el resultado de la última operación de entrada/salida (I/O) de ficheros.
  • Read - Lee desde un fichero tipo texto.
  • ReadLn - Lee desde un fichero tipo texto (una línea entera) y salta a la siguiente línea.
  • Reset - Abre un fichero para lectura.
  • Rewrite - Crea un fichero para escritura.
  • Seek - Cambia a una posición dentro del fichero.
  • SeekEOF - Sitúa la posición dentro del fichero en su final.
  • SeekEOLn - sitúa la posición del fichero al final de la línea.
  • Truncate - Trunca el fichero en la posición indicada.
  • Write - Escribe una variable en el fichero.
  • WriteLn - Escribe una variable a un fichero de texto y salta a una nueva línea.

Ejemplo

Un ejemplo completo de manejo de un fichero de texto del tipo 'TextFile':


program CreateFile;

uses
 Sysutils;

const
  C_FNAME = 'textfile.txt';

var
  tfOut: TextFile;

begin
  // Establece el nombre del fichero que vamos a crear
  AssignFile(tfOut, C_FNAME);

  // Habilitamos el uso de excepciones para interceptar los errores (esto es como está por defecto por lo tanto no es absolutamente requerido)

  {$I+}

  // Embebe la creación del fichero en un bloque try/except para manejar los errores elegántemente.
  
  try
  
    //crea el fichero, escribe algo de texto y lo cierra.
    rewrite(tfOut);

    writeln(tfOut, 'Hello textfile!');
    writeln(tfOut, 'The answer to life, the universe and everything: ', 42);

    CloseFile(tfOut);

  except
    // Si ocurre algún error podemos encontrar la razón en E: EInOutError 
      writeln('Ha ocurrido un error en el manejo del fichero. Detalles: ', E.ClassName, '/', E.Message);
  end;

  // Da información y espera por la pulsación de una tecla.
  writeln('Fichero ', C_FNAME, ' creado si todo fue bien. Presiona Enter para finalizar.');
  readln;
end.

Ahora abre el fichero generado utilizando para ello cualquier editor y verás justamente el texto que hemos escrito mediante código. Puedes probar el manejo de errores ejecutando el programa de nuevo, en esta ocasión la prueba que puedes realizar es establecer el atributo del fichero en modo solo lectura de forma que el programa no pueda hacer el rewrite y ver el error que nos lanza. Now open the file in any text editor and you will see the above text written to it!

Nota que el manejo de excepciones se ha utilizado como una manera fácil de realizar múltiples operaciones con ficheros y manejar los errores que obtengamos como resultado. También puedes utilizar {$I-}, pero en este caso hay que consultar el valor almacenado en la variable IOResult después de cada operación y modificar tu próxima operación para tratar el posible error.

Lo siguiente muestra como se puede añadir texto a un fichero del tipo textfile (al final del contenido ya existente):

program AppendToFile;

uses
 Sysutils;

const
  C_FNAME = 'textfile.txt';

var
  tfOut: TextFile;

begin
  // Establece el nombre del fichero que va a recibir más texto.
  AssignFile(tfOut, C_FNAME);

  // Embebe el manejo del fichero en un bloque try/except para gestionar los errores de manera elegante.
  try
    // Abre el fichero en la modalidad añadir (appending), escrie algo de texto y lo cierra.
    append(tfOut);

    writeln(tfOut, ' Hola de nuevo fichero de texto. ');
    writeln(tfOut, 'El resultado de 6 * 7 = ', 6 * 7);

    CloseFile(tfOut);

  except
    on E: EInOutError do
     writeln('Ha ocurrido un error en el manejo del fichero. Detalles: ', E.Message);
  end;

  // Da información y espera a que se pulse una tecla.
  writeln('Fichero ', C_FNAME, ' puede contener más texto. Presiona Enter para finalizar.');
  readln;
end.

Leyendo un fichero de texto (textfile):

program ReadFile;

uses
 Sysutils;

const
  C_FNAME = 'textfile.txt';

var
  tfIn: TextFile;
  s: string;

begin
  // Da algo de información
  writeln('Leyendo el contenido del fichero: ', C_FNAME);
  writeln('=========================================');

  // Establece el nombre del fichero a leer.
  AssignFile(tfIn, C_FNAME);

  // Embebe el manejo del fichero en un bloque try/except para manejar errores de manera elegante.
 try
    // Abre el fichero en la modalidad de lectura.
    reset(tfIn);

    // Se mantiene leyendo líneas hasta alcanzar el final del fichero.
    while not eof(tfIn) do  // Mientras no fin de fichero haz...
    begin
      readln(tfIn, s); // Lee una línea de texto desde el fichero.
      writeln(s);  // Escribe la línea de texto leida anteriormente mostrándola en pantalla.
    end;

    // Realizado, por tanto prcedemos a cerrar el fichero.
    CloseFile(tfIn);

  except
    on E: EInOutError do
     writeln('Ha ocurrido un error en el manejo del fichero. Detalles: ', E.Message);
  end;

  // Espera la intervención del usuario para finalizar el programa.
  writeln('=========================================');
  writeln('fichero ', C_FNAME, ' fue probablemente leido. Presiona Enter para finalizar.');
  readln;
end.

Object style

In addition to the old style file handling routines mentioned above, a new system exists that uses the concept of streams (- of data) at a higher abstraction level. This means data can be read from or written to any location (disk, memory, hardware ports etc.) by one uniform interface.

In addition, most string handling classes have the ability to load and save content from/to a file. These methods are usually named SaveToFile and LoadFromFile. A lot of other objects (such as Lazarus grids) have similar functionality, including Lazarus datasets (DBExport). It pays to look through the documentation or source code before trying to roll your own save/load routines.

Binary files

For opening files for direct access TFileStream can be used. This class is an encapsulation of the system procedures FileOpen, FileCreate, FileRead, FileWrite, FileSeek and FileClose which resides in unit SysUtils.

IO routines

In the example below, note how we encapsulate the file handling action with a try..except block so that errors are handled correctly just as with the classic file handling routines (as not to convolute the example the fsOut.write is not put inside a try...finally block).

program WriteBinaryData;
{$mode objfpc}

uses
  Classes, Sysutils;

const
  C_FNAME = 'binarydata.bin';

var
  fsOut    : TFileStream;
  ChrBuffer: array[0..2] of char;

begin
  // Set up some random data that will get stored
  ChrBuffer[0] := 'A';
  ChrBuffer[1] := 'B';
  ChrBuffer[2] := 'C';

  // Catch errors in case the file cannot be created
  try
    // Create the file stream instance, write to it and free it to prevent memory leaks
    fsOut := TFileStream.Create( C_FNAME, fmCreate);
    fsOut.Write(ChrBuffer, sizeof(ChrBuffer));
    fsOut.Free;

  // Handle errors
  except
    on E:Exception do
      writeln('File ', C_FNAME, ' could not be created because: ', E.Message);
  end;

  // Give feedback and wait for key press
  writeln('File ', C_FNAME, ' created if all went ok. Press Enter to stop.');
  readln;
end.


You can load entire files into memory too if its size is comparatively smaller than available system memory. Bigger sizes might work but your operating system will start using the page/swap file, making the exercise useless from a performance standpoint.

program ReadBinaryDataInMemoryForAppend;
{$mode objfpc}

uses
  Classes, Sysutils;

const
  C_FNAME = 'binarydata.bin';

var
  msApp: TMemoryStream;

begin
  // Set up the stream
  msApp := TMemoryStream.Create;

  // Catch errors in case the file cannot be read or written
  try
    // Read the data into memory
    msApp.LoadFromFile(C_FNAME);

    // Seek the end of the stream so data can be appended
    msApp.Seek(0, soEnd);

    // Write some arbitrary data to the memory stream
    msApp.WriteByte(68);
    msApp.WriteAnsiString('Some extra text');
    msApp.WriteDWord(671202);

    // Store the data back on disk, overwriting the previous contents
    msApp.SaveToFile(C_FNAME);

  // Handle errors
  except
    on E:Exception do
      writeln('File ', C_FNAME, ' could not be read or written because: ', E.Message);
  end;

  // Clean up
  msApp.Free;

  // Give feedback and wait for key press
  writeln('File ', C_FNAME, ' was extended if all went ok. Press Enter to stop.');
  readln;
end.

With larger files of many Gb, you may want to read in buffers of, say, 4096 bytes (you're advised to use a multiple of the filesytem cluster or block size) and do something with the data of each buffer read.

var
  TotalBytesRead, BytesRead : Int64;
  Buffer : array [0..4095] of byte;  // or, array [0..4095] of char
  FileStream : TFileStream;

try
  FileStream := TFileStream.Create;
  FileStream.Position := 0;  // Ensure you are at the start of the file
  while TotalBytesRead <= FileStream.Size do  // While the amount of data read is less than or equal to the size of the stream do
  begin
    BytesRead := FileStream.Read(Buffer,sizeof(Buffer));  // Read in 4096 of data
    inc(TotalBytesRead, BytesRead);                       // Increase TotalByteRead by the size of the buffer, i.e. 4096 bytes
    // Do something with Buffer data
  end;

FileCopy

Con lo aprendido anteriormente podemos implementar una función simple de copia de ficheros (FreePascal no tiene ninguna en su RTL aunque Lazarus tiene copyfile) - se puede ajustar para ficheros de mayor tamaño, etc:

program FileCopyDemo;
// Demonstración de la función FileCopy.

{$mode objfpc}

uses
  classes;

const
  fSource = 'test.txt';
  fTarget = 'test.bak';

function FileCopy(Source, Target: string): boolean;
// Copia fuente a destino, sobreescribe destino.
// Cachea el contenido completo en memoria.
// Retorna true si tiene exito; falso en caso contrario.urns true if succeeded; false if failed.
var
  MemBuffer: TMemoryStream;
begin
  result := false;
  MemBuffer := TMemoryStream.Create;
  try
    MemBuffer.LoadFromFile(Source);
    MemBuffer.SaveToFile(Target); 
    result := true
  except
    //swallow exception; function result is false by default
  end;
  // Clean up
  MemBuffer.Free
end;

begin
  If FileCopy(fSource, fTarget)
    then writeln('File ', fSource, ' copied to ', ftarget)
    else writeln('File ', fSource, ' not copied to ', ftarget);
  readln()
end.

Handling Text files (TStringList)

In general, for text files you can use the TStringList class to load the entire file into memory and have easy access to its lines. Of course, you can also write the StringList back to a file:

program StringListDemo;
{$mode objfpc}

uses
  Classes, SysUtils;

const
  C_FNAME = 'textfile.txt';

var
  slInfo: TStringList;

begin
  // Create an instance of the string list to handle the textfile
  slInfo := TStringList.Create;

  // Embed the file handling in a try/except block to handle errors gracefully
  try
    // Load the contents of the textfile completely in memory
    slInfo.LoadFromFile(C_FNAME);

    // Add some more contents
    slInfo.Add('An extra line appended to the text');
    slInfo.Add('And another one.');
    slInfo.Add('Let''s stop here.');
    slInfo.Add('It is now ' + DateTimeToStr(now));

    // And write the contents back to disk, replacing the original contents
    slInfo.SaveToFile(C_FNAME);

  except
    // If there was an error the reason can be found here
    on E: EInOutError do
      writeln('File handling error occurred. Reason: ', E.Message);
  end;

  // Clean up
  slInfo.Free;

  // Give feedback and wait for key press
  writeln('File ', C_FNAME, ' updated if all went ok. Press Enter to stop.');
  readln;
end.

Demo: save single string to a file

In order to write a single string to a stream you might want to use the procedure defined below. Note that strings in FreePascal can be extremely long, this is also a useful way to write a big block of textdata to a file.

program SaveStringToPathDemo;
{$mode objfpc}

uses
  Classes, sysutils;

const
  C_FNAME = 'textstringtofile.txt';

// SaveStringToFile: function to store a string of text into a diskfile.
//   If the function result equals true, the string was written ok.
//   If not then there was some kind of error.
function SaveStringToFile(theString, filePath: AnsiString): boolean;
var
  fsOut: TFileStream;
begin
  // By default assume the writing will fail.
  result := false;

  // Write the given string to a file, catching potential errors in the process.
  try
    fsOut := TFileStream.Create(filePath, fmCreate);
    fsOut.Write(theString[1], length(theString));
    fsOut.Free;

    // At his point it is known that the writing went ok.
    result := true

  except
    on E:Exception do
      writeln('String could not be written. Details: ', E.ClassName, ': ', E.Message);
  end
end;

//
// Main program
//
begin
  // Try to save a simple textstring in a file and give feedback of sucess.
  if SaveStringToFile('>> this text gets stored <<', C_FNAME) then
    writeln('Text succesfully written to a file')
  else
    writeln('Writing text to a file failed.');

  // Wait for the user to press Enter
  readln
end.