GNU-Pascal in Beispielen: Dateien

zurück zu GNU-Pascal in Beispielen

DateienBearbeiten

Dateien dienen dazu, Informationen dauerhaft auf Medien zu speichern. In GNU-Pascal wird unterschieden zwischen Textdateien und allgemeinen Dateien, wobei sich dieser Unterschied nur darin zeigt, mit welchen Routinen die Datei gelesen oder beschrieben wird. Das Besondere an Dateivariablen ist, dass man sie einander nicht zuweisen kann.

Aus Textdateien lesenBearbeiten

Das folgende Beispiel zeigt, wie eine Textdatei, in diesem Fall /etc/passwd, ausgelesen wird:

Programm: LesenBearbeiten

program Lesen;

var
  Datei: Text;
  Zeile: String (1000);
  AnzahlZeilen: Integer = 0;

begin
  Assign (Datei, '/etc/passwd');
  Reset (Datei);
  while not EOF (Datei) do
    begin
      ReadLn (Datei, Zeile);
      WriteLn (Zeile);
      Inc (AnzahlZeilen)
    end;
  Close (Datei);
  WriteLn ('Habe ', AnzahlZeilen, ' Zeilen gelesen.')
end.

ErklärungBearbeiten

Der Datentyp Text ist ein Typ für beliebige Textdateien. Mit der Prozedur Assign wird eine namentlich bekannte Datei einer Variablen zugewiesen. Diese Datei kann nun mit Reset zum lesen geöffnet werden. EOF ist eine Funktion, die True ergibt, wenn das Ende der Datei erreicht ist. ReadLn trägt ein Dateiargument, um mitzuteilen, aus welcher Datei eine Datenzeile gelesen werden soll. Mit Close wird die Datei wieder geschlossen.


In Textdateien schreibenBearbeiten

Das folgende Beispiel zeigt, wie in eine Textdatei geschrieben [1] wird:

Programm: SchreibenBearbeiten

program Schreiben;

var
  Datei: Text;
  Zeile: String (1000);

begin
  Assign (Datei, 'namen.text');
  Rewrite (Datei);
  Write ('Wie lautet ihr Name? ');
  ReadLn (Zeile);
  WriteLn (Datei, Zeile);
  Close (Datei)
end.


ErklärungBearbeiten

In bekannter Weise wird mit Assign ein Dateiname einer Datei zugewiesen. Diese Datei wird mit Rewrite zum Schreiben geöffnet. Eine Textzeile wird mit Hilfe von WriteLn in die Datei geschrieben, wobei auch hier ein zusätzlicher Parameter die Zieldatei angibt.


Vordefinierte TextdateienBearbeiten

Es existieren drei vordefinierte Textdateien, die Standard-Eingabe, Standard-Ausgabe und eine Standard-Fehlerdatei. Die ersten beiden Dateien benutzen sie immer, wenn Sie Texte mit ReadLn einlesen oder mit WriteLn ausgeben. Die dritte Datei muss explizit angesprochen werden:

Programm: VordefBearbeiten

program Vordef;

begin
  WriteLn ('Normale Ausgabe');
  WriteLn (StdErr, 'Fehlerausgabe')
end.

ErklärungBearbeiten

Beide Strings werden in unterschiedliche Dateien geschrieben, allerdings merken Sie davon nichts, wenn Sie das Programm nur ausführen. Erst mit vordef 2>/dev/null, wobei Sie die Zwei [2] auch wahlweise durch Eins ersetzen können, ändert sich die Ausgabe. StdErr bezeichnet diesen Fehlerkanal, auf den Sie Fehlermeldungen ausgeben können.

Schreiben und Lesen von allgemeinen DateienBearbeiten

Das Schreiben und Lesen von allgemeinen Dateien erfolgt analog zum Schreiben und Lesen von Textdateien:

Programm: AllgdateiBearbeiten

program Allgdatei;

var
  Datei: File;
  i, Wert: Integer;

begin
  Assign (Datei, 'werte.dat');

  { Schreiben }
  Rewrite (Datei, SizeOf (Integer));
  for i := 0 to 10 do
    BlockWrite (Datei, i, 1);
  Close (Datei);

  { Lesen }
  Reset (Datei, SizeOf (Integer));
  for i := 0 to 10 do
    begin
      BlockRead (Datei, Wert, 1);
      WriteLn ('Wert = ', Wert)
    end;
  Close (Datei)
end.

ErklärungBearbeiten

Der Datentyp File ist der Typ einer allgemeinen Datei. Geöffnet wird eine solche Datei zum Schreiben mit Rewrite, wobei als weiterer Parameter die Größe der Daten, die in die Datei geschrieben werden sollen, anzugeben ist. Geschrieben wird in eine solche Datei mit BlockWrite, wobei das erste Argument die Datei ist, das Zweite die Daten und das dritte Argument die Anzahl der Daten, die geschrieben werden sollen, bezeichnet. Reset hat ebenfalls einen zusätzlichen Parameter, der die Blockgröße der zu lesenden Daten angibt. BlockRead liest aus dieser Datei und hat dieselben Argumente wie BlockWrite.

Dateien als ProgrammparameterBearbeiten

Dateien lassen sich auch im Kopf eines Programms deklarieren. Damit bekommen sie eine Bedeutung, die mit oben erklärten Techniken nicht zu implementieren ist:

Programm: ZeilenZahlBearbeiten

program ZeilenZahl (Input);

var
  Zeile: String (1000);
  AnzahlZeilen: Integer = 0;

begin
  Reset (Input);
  while not EOF (Input) do
    begin
      ReadLn (Input, Zeile);
      Inc (AnzahlZeilen)
    end;
  WriteLn ('Habe ', AnzahlZeilen, ' Zeilen gelesen.');
  Close (Input)
end.

ErklärungBearbeiten

Wenn Sie dieses Programm ausführen, so erwartet es Eingaben von Ihnen, bis Sie STRG+D drücken. Viel interessanter ist es aber, das Programm als Filter einzusetzen und durch das Programm hindurchzupipen: cat zeilenzahl.pas | zeilenzahl. Dieses Programm behandelt die ihr übergebenen Textdaten als Datei mit unbekanntem Dateinamen, jedoch bekannter Dateivariablen Input. Diese Datei kann zum Lesen geöffnet werden. Hinter Input dürfen noch weitere Dateivariablen stehen.


Schreiben und Lesen von typbehafteten DateienBearbeiten

Während man in allgemeine Dateien beliebige Daten ablegen kann und Textdateien zur Speicherung von Text dienen, gibt es in typisierten Dateien die Möglichkeit, eine Menge Daten vom selben Typ abzulegen:

Programm: TypisiertBearbeiten

program Typisiert;

var
  Datei: File of Integer;
  i: Integer;

begin
  Assign (Datei, 'foo.dat');

  Rewrite (Datei);
  for i := 0 to 10 do
    Write (Datei, i);
  Close (Datei);

  Reset (Datei);
  while not EOF (Datei) do
    begin
      Read (Datei, i);
      WriteLn (i)
    end;
  Close (Datei)
end.

ErklärungBearbeiten

In diesem Beispiel haben wir eine Datei erzeugt, die nur aus Integer-Werten bestehen kann. Dies erreichen wir, indem bei der Variablendeklaration der Datei der zu speichernde Datentyp angegeben wird. In diese Datei kann mit Write geschrieben und mit Read aus ihr gelesen werden. Bei der Verwendung dieser Dateisorte ist es nicht notwendig, Rewrite oder Reset zusätzliche Parameter mitzugeben.


Programmierbeispiel: AdressdateiBearbeiten

Das folgende Programmierbeispiel implementiert eine kleine Adressdatenbank. Sie werden aufgefordert, beliebig viele Adressen einzugeben. Anschließend wird die komplette Datenbank ausgegeben. Als Besonderheit ist das Programm so angelegt, dass die Daten bei einem erneuten Durchlauf nicht verloren gehen, sondern immer weiter angehängt werden:

Programm: AdressedatBearbeiten

program Adressedat;

type
  TEintrag = String (30);
  TPlz     = String (5);
  TAdresse = record
    Vorname, Name, Strasse: TEintrag;
    Plz: TPlz;
    Stadt: TEintrag
  end;
  TAdressDatei = File of TAdresse;

var
  Datei: TAdressDatei;
  Adresse: TAdresse;
  Eingabe: Char;

function FrageAdresse: TAdresse;
var
  Anschrift: TAdresse;
begin
  with Anschrift do
    begin
      Write ('Vorname: ');
      ReadLn (Vorname);
      Write ('Name: ');
      ReadLn (Name);
      Write ('Strasse: ');
      ReadLn (Strasse);
      Write ('Postleitzahl: ');
      ReadLn (Plz);
      Write ('Stadt: ');
      ReadLn (Stadt)
    end;
  FrageAdresse := Anschrift
end;

procedure DruckeAdresse (Anschrift: TAdresse);
begin
  with Anschrift do
    begin
      WriteLn (Vorname, ' ', Name);
      WriteLn (Strasse);
      WriteLn (Plz, '-', Stadt)
    end
end;

procedure SchreibeAdresse (var AdressDatei: TAdressDatei; Anschrift: TAdresse);
begin
  Write (AdressDatei, Anschrift)
end;

procedure LiesAdresse (var AdressDatei: TAdressDatei; var Anschrift: TAdresse);
begin
  Read (AdressDatei, Anschrift)
end;

begin
  Assign (Datei, 'adressen.dat');
  Append (Datei);
  repeat
    Adresse := FrageAdresse;
    SchreibeAdresse (Datei, Adresse);
    Write ('Eine weitere Adresse eingeben (j/n): ');
    ReadLn (Eingabe)
  until Eingabe in ['n', 'N'];
  Close (Datei);
  
  { Adressen ausgeben }
  WriteLn ('=== Adressen ===');
  Assign (Datei, 'adressen.dat');
  Reset (Datei);
  while not EOF (Datei) do
    begin
      LiesAdresse (Datei, Adresse);
      DruckeAdresse (Adresse)
    end;
  Close (Datei)
end.

ErklärungBearbeiten

Es wird ein Typ einer typenbehafteten Datei definiert, der in den Funktionen genutzt werden kann. Dateivariablen müssen immer im Original, also Call by Reference, übergeben werden. Die Routine Append öffnet eine Datei zum Anhängen von Daten.


AnmerkungenBearbeiten

  1. Bitte beachten Sie, dass diese Datei in Ihrem gewählten Verzeichnis nicht existieren darf, sonst wird sie überschrieben!
  2. Hintergrund: 1 bedeutet auf der Shell die Standard-Ausgabe während 2 den Standard-Fehlerkanal bezeichnet. Der Teil 2>/dev/null leitet die Ausgabe des Fehlerkanals so um, dass Sie von der Fehlermeldung nichts zu sehen bekommen. Genauso verfährt man mit der Standard-Eingabe.