File Handling In Pascal/fr

From Lazarus wiki
Jump to navigationJump to search

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

Une chose que doit savoir faire un développeur est de travailler avec des fichiers. Les fichiers servent à garder les réglages utilisateur, les rapports d'erreur et plus encore. Ici, je vais vous apprendre comme travailler avec des simples fichiers texte.

Vieux style porocédural

Lorsque vous utilisez des fichiers en Pascal classique (non orienté objet), vous pouvez utiliser un type TextFile qui vous permet d'écrire de la chaîne dans le fichier ou créer votre propre type de fichier.

...
type
 TIntegerFile = file of Integer; // Vous permet d'écrire des entiers dans le fichier
 TPCharFile = file of PChar; // Ecrire des PChars dans le fichier :\
 TStringFile = file of String; // Ecrire des chaînes dans le fichier
...

Si nous faisons uniquement TStringFile = File, alors il serait impossible d'écrire quelque chose dedans! Aussi, vous ne pouvez pas écrire des entiers directement dans TStringFile parce que c'est un fichier de chaînes.

IO

IO est le truc de la gestion des fichiers pour Pascal. Il dit au compilateur comment traiter les situations d'erreurs d'E/S: lever une exception ou stocker le code d'erreur dans la variable IOResult. Puisqu'il s'agit d'une directive de compilation, vous devez faire ceci:

{$I-} // Désactive le contrôle. De cette façon, toutes les erreurs vont dans la variable IOResult
{$I+} // Remets le contrôle; les erreurs vont mener à une exception EInOutError.

En désactivant/éteignant $I, les résultats des opérations de fichier vont dans la variable IOResult. C'est un type cardinal (nombre). Si vous voulez écrire l'erreur, vous utilisez la fonction IntToStr. Différents nombres signifie différentes erreurs. Aussi, vous pouvez vouloir vérifier la documentation pour les différentes erreurs: [1].

Procédures pour les fichiers

Ces procédures et fonctions de manipulation de fichiers sont placées dans l'unité system, elles sont donc toujours disponibles sans avoir à déclarer cette unité dans votre programme. Voir la documentation de FPC pour plus de détails: Reference for 'System' unit

  • Assign - Affecte un nom au fichier
  • Append - Ouvre un fichier existant pour ajouter des données à la fin et l'éditer
  • BlockRead - Lit les données d'un fichier non typé dans la mémoire
  • BlockWrite - Ecrit des données de la mémoire vers un fichier non typé
  • Close - Ferme un fichier ouvert
  • EOF - Vérifie si la fin du fichier est atteinte
  • Erase - Efface un fichier du disque
  • FilePos - Retourne la position courante dans un fichier
  • FileSize - Retourne la taille du fichier
  • Flush - Ecrit les tampons de fichier sur le disque
  • IOResult - Retourne le résultat de la dernière opération d'E/S fichier
  • Read - Lit dans une variable depuis un fichier
  • ReadLn - Lit dans une variable depuis un fichier et va à la prochaine ligne
  • Reset - Ouvre un fichier en lecture
  • Rewrite - Ouvre un fichier en écriture
  • Seek - Change la position courante dans le fichier
  • SeekEOF - Met la position courante en fin de fichier
  • SeekEOLn - SMet la position courante en fin de ligne
  • Truncate - Tronque le fichier à la position courante
  • Write - Ecrit la variable dans un fichier texte
  • WriteLn - Ecrit la variable dans un fichier texte et ajoute une nouvelle ligne (vide)


Exemple

Un exemple complet de manipulation de fichier texte:

program FileTest;

{$mode objfpc} // Ne m'oublier jamais

uses
 Sysutils;

var
 FileVar: TextFile;

begin
  WriteLn('Test de fichier');
  AssignFile(FileVar, 'Test.txt'); // Vous n'avez pas à mettre. Txt, mais c'est juste pour l'instant
  {$I+} //Utilise les exceptions
  try  
    Rewrite(FileVar);  // Créer le fichier
    Writeln(FileVar,'Salut');
    CloseFile(FileVar);
  except
    on E: EInOutError do
    begin
     Writeln('Une erreur de manipulation de fichier s''est produite. Détails: '+E.ClassName+'/'+E.Message);
    end;    
  end;
  WriteLn('Programme terminé. Pressez Entrée pour arrêter.');  
  ReadLn;
end.

Maintenant ouvrez le fichier avec un éditeur de texte (Bloc-note...), vous verrez écrit Salut dedans! Vous pouvez tester la gestion des erreurs en mettant test.txt en lecture seule et en rejouant le programme.

Remarquez que nous avons utilisé la gestion des exceptions ({$I+}) car c'est plus facile pour réaliser de multiples opérations de fichiers et pour gérer les erreurs. Vous pouvez aussi utiliser {$I-}, mais vous aurez à vérifier IOResult après chaque opération et modifier votre prochaine opération.

Voici comment l'ajout en fin/ajout dans un fichier fonctionne:

program EditFile;

{$mode objfpc}

uses
 Sysutils;

var
 File1: TextFile;
 
begin
  WriteLn('Ajout en fin de fichier');
  {$I+}
  try
    AssignFile(File1, 'File.txt');
    Append(File1, 'Ajouter du texte...');
    CloseFile(File1);
  except
    on E: EInOutError do
    begin
     Writeln('Une erreur de gestion de fichier s''est produite. Détails: '+E.ClassName+'/'+E.Message);
    end;    
  end;
  WriteLn('Programme terminé. Pressez Entrée pour arrêter.');  
  ReadLn;
end.

Lecture d'un fichier:

program ReadFile;

{$mode objfpc}

uses
 Sysutils;

var
 File1: TextFile;
 Str: String;
 
begin
  Writeln('Lecture de fichier:');
  AssignFile(File1, 'File.txt');
  {$I+}
  try
    Reset(File1);
    repeat
      Readln(File1, Str); // Lit la ligne entière depuis le fichier
      Writeln(Str); // Ecrit la ligne lue à l'écran
    until(EOF(File1)); // EOF(Fin de fichier) le programme continue à lire des nouvelles jusqu'à la fin
    CloseFile(File1);
  except
    on E: EInOutError do
    begin
     Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
    end;    
  end;
  WriteLn('Programme terminé. Pressez Entrée pour arrêter.');  
  ReadLn;
end.

Il est possible de faire un peu de manipulation de fichiers en utilisant les caractères au lieu de chaînes. Cela rend l'air cool: D.

Style objet

En plus des routines de manipulation de fichier vieux style mentionnées ci-dessus, un nouveau système existe qui utilise le concept des flux (flux de donnée) à un niveau d'abstraction plus élevé, ce qui veut dire que vous en tant que programmeur avez moins de chose à faire pour traiter les fichiers.

En plus, la plupart des classes de manipulation de chaînes ont la capacité de charger ou sauver du contenu depuis/vers un fichier. Ces méthodes sont appelées généralement LoadFromFile et SaveToFile. Beaucoup d'autres objets (tels les grilles de Lazarus) ont des fonctionnalités similaires, comprenant les ensembles de données (DBExport); cela vaut le coup de regarder la documentation du code source avant d'essayer de faire vos propres routines.

Fichiers binaires

Pour l'ouverture des fichiers en accès direct, TFileStream devrait être utilisé. Cette classe est une encapsulation des procédures FileOpen, FileCreate, FileRead, FileWrite, FileSeek et FileClose qui se trouvent dans l'unité FileUtil.

IO routines

Dans l'exemple du dessous; remarquez comment nous encadrons l'action de manipulation de fichier avec un bloc try..finally. Cela assure que le l'objet TFileStream est toujours libéré (la partie finally...) même s'il y a une erreur d'accès au fichier (ou autre).

var
  Buffer: array[0..9999] of Byte;
begin
  with TFileStream.Create('SomeFile.bin', fmCreate) do 
  try
    Seek('Salut');
    Write(Buffer, SizeOf(Buffer));
  finally
    Free;
  end;
end;


Vous pouvez charger les fichiers entiers dans la mémoire s'ils sont comparativement plus petits que la mémoire système. De plus grosses tailles pourraient marcher mais le système d'exploitation commencera à utiliser la mémoire virtuelle (swap), rendant l'exercice inutile du point de vue des performances.

begin
  with TMemoryStream.Create do 
  try
    LoadFromFile('SomeFile.bin');
    Seek(0, soEnd);
    Write(Ord('A'), 1);
    SaveToFile('SomeFile.bin');
  finally
    Free;
  end;
end;

Avec des fichiers de plusieurs Go, vous voudriez lire dans des tampons de, disons, 4096 octets (il est conseillé de choisir un multiple du secteur du système de fichier ou de la taille de bloc) et faire quelque chose avec les données de chaque tampon lu.

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

try
  FileStream := TFileStream.Create;
  FileStream.Position := 0;  // Garantir que vous êtes en début de fichier 
  while TotalBytesRead <= FileStream.Size do  // Tant que la qté de donnée lue est <= la taille du flux, faire
  begin
    BytesRead := FileStream.Read(Buffer,sizeof(Buffer));  // Lire 4096 octet de donnée
    inc(TotalBytesRead, BytesRead);                       // Ajouter BytesRead à TotalByteRead, i.e. 4096 octets
    // Faire qque chose avec les données
  end;

FileCopy

Avec ce qui précède, nous pouvons implémenter une simple fonction de copie de fichier (FreePascal n' pas une telle fonction dans sa RTL, bien que Lazarus en a), ajuster comme il se doit pour les gros fichiers

function FileCopy(Source, Target: string): boolean;
// Copie la source dans la cible (target); écrase la cible d'avant.
// Copie le fichier entier en mémoire.
// Retourne true en cas de succès; false sinon
var
  MemBuffer: TMemoryStream;
begin
  result:=false;
  MemBuffer:=TMemoryStream.Create;
  try
    try
      MemBuffer.LoadFromFile(Source);
      MemBuffer.Position:=0;
      MemBuffer.SaveToFile(Target); //peut être le même que la source
      result:=true;
    except
      result:=false; //avale l'exception, convertit en code d'erreur
    end;
  finally
    MemBuffer.Free;
  end;
end;

Fichiers Texte

En général, pour les fichiers texte, vous pouvez utiliser la classe TStringList pour charger le fichier entier en mémoire et avoir un accès simple à ses lignes. Bien sûr, vous pouvez aussi sauver la StringList dans le fichier:

begin
  with TStringList.Create do 
  try
    Add('Salut');
    SaveToFile('SomeFile.txt');
  finally
    Free;
  end;
end;

Pour écrire une simple ligne dans un flux, vous pourriez utiliser la procédure suivante:

procedure SaveStringToPath(theString, filePath: String);
var
  textFile: TFileStream = nil;
  textLength: integer;
  stringBuffer: ^String;
begin
  textLength := length(theString);
  try
    textFile := TFileStream.Create(filePath, fmOpenWrite or fmCreate);
    { écrire la chaîne dans une flux tout en évitant d'écrire la longueur initiale }
    stringBuffer := @theString + 1;
    textFile.WriteBuffer(stringBuffer^, textLength);
  finally
    if textFile <> nil then textFile.Free;
  end;
end;

Voir aussi