Ausschnitt aus der Ada-Version des Buchs Objektorientiertes Plug und Play
© Prof. Dr. Andreas Solymosi

Sequentielle Dateien

Ein häufig benutzter Spezialfall der positionierbaren Liste ist eine sequentielle Datei . Hierbei werden die Navigation, das Lesen und das Schreiben stark eingeschränkt:

Eine Datei behält also die Daten in der Reihenfolge der Eintragung. Die Eintragung muß abgeschlossen werden, bevor die Datei gelesen werden kann. Das Lesen erfolgt in der selben Reihenfolge, wie die Eintragung stattfand. Möchte man zu einem schon gelesenen Element zurück, muß das Lesen von vorne angefangen werden:

  W1 W2 ... Wi Wi+1 ... Wn   Datei
       

­

®

      Position
         

­

      Lesen
 

­

              Zurücksetzen

Sequentielle Datei

Die Paketschablone GSeq_Datei exportiert einen abstrakten Dateityp.

generic -- aus der Datei GSeqdat.ads
	type TElement is private;
package GSeq_Datei is
	type TSeq_Datei is limited private;
	procedure Alles_loeschen (Datei: out TSeq_Datei); -- macht die Datei leer, bereit zum Beschreiben
	procedure Zuruecksetzen (Datei: in out TSeq_Datei); -- macht die Datei bereit zum Lesen
		-- trägt Element an das Ende der Datei ein; nur im Schreibmodus:
	procedure Eintragen (Datei: in out TSeq_Datei; Element: in TElement); -- raises:
	ESpeicher_voll, ELesemodus: exception;
		-- Ausnahme ELesemodus, wenn Alles_loeschen gar nicht oder nicht nach Zuruecksetzen aufgerufen wurde
	procedure Naechstes_Element (Datei: in out TSeq_Datei); -- schreitet zum nächsten Element; raises:
	EDateiende, ESchreibmodus: exception;
		-- Ausnahme ESchreibmodus, wenn Zuruecksetzen gar nicht oder nicht nach Alles_loeschen aufgerufen wurde
		-- Ausnahme EDateiende, falls Naechstes_Element öfters als Eintragen aufgerufen wurde
	function Ende_der_Datei (Datei: TSeq_Datei) return Boolean; -- raises ESchreibmodus;
		 -- True wenn der Aufruf Naechstes_Element die Ausnahme EDateiende auslösen würde; nur im Lesemodus
	function Aktuelles_Element (Datei: TSeq_Datei) return TElement;
		-- raises EDateiende, ESchreibmodus;
		-- liefert das Element an der aktuellen Position; nur im Lesemodus
	procedure Kopieren (Ziel: out TSeq_Datei; Quelle: in TSeq_Datei); -- raises ESpeicher_voll;
	function "=" (Links, Rechts: TSeq_Datei) return Boolean;
	-- diesmal kein Iterator, keine Persistenzoperationen
	...

Wie aus der Schnittstelle ersichtlich ist, kann eine Datei nur beschrieben werden, wenn sie mit Alles_loeschen zum Schreiben geöffnet wurde. Die mit Eintragen übergebenen Elemente werden der Reihe nach in die Liste eingetragen. Anschließend kann die Datei mit Zuruecksetzen zum Lesen geöffnet werden. Ein Aufruf der Funktion Aktuelles_Element liefert dann das älteste (als erstes eingetragene) Element der Datei. Mit dem Aufruf Naechstes_Element kann man zum nächsten weiterschreiten, falls es noch welche gibt. Die Ausnahme EDateiende kann durch die Abfrage der logischen Funktion Ende_der_Datei vorgebeugt werden.

Der Kommentar "Ausnahme EDateiende, falls Naechstes_Element öfters als Eintragen aufgerufen wurde" unterscheidet einen Datenbehälter vom Typ TSeq_Datei von dem, was man üblicherweise unter einer Datei versteht. Er impliziert nämlich, daß der Datenbehälter zuerst gefüllt werden muß, bevor er gelesen werden kann. Eine auf externen Datenträgern vorliegende Datei kann sofort gelesen werden. Man sagt, Datenbehälter vom Typ TSeq_Datei besitzen - im Gegensatz zu TPos_Liste - keine Persistenz.

Übung: Implementieren Sie GSeq_Datei mit Hilfe der Paketschablone GPos_Liste.


Sequentielle Dateien in Ada

Die bisherigen Multibehälter müssen mit einem extra Operationsaufruf Speichern persistent gemacht werden. Persistente Dateien sind von sich aus persistent, d.h. das Eintragen in die Datei alleine garantiert schon die Übertragung des Elements auf den externen Datenträger.

Das mit jedem Ada-Compiler ausgelieferte Paket Sequential_IO exportiert einen persistenten Dateityp. Ein Teil seiner Schnittstelle gibt Auskunft über die Benutzungsmöglichkeiten:

generic
	type Element_Type is private; -- ausprägbar für jeden nicht-eingeschränkten Datentyp
package Sequential_IO is
	type File_Type is limited private; -- der exportierte abstrakte Datentyp
	type File_Mode is (In_File, Out_File); -- Öffnungsmodus für Ein- oder Ausgabe
	procedure Open (File: in out File_Type; Mode: in File_Mode := Out_File;
		Name: in String := ""; Form: in String := "");
	procedure Read (File: in File_Type; Item: out Element_Type);
	procedure Write (File: in File_Type; Item: in Element_Type);
	function End_of_File (File: File_Type) return Boolean;
	End_Error: exception;
		...

Der Operation Alles_loeschen entspricht hier die Operation Open mit dem Parameter Mode => Out_File. Write ersetzt Eintragen. Anstelle von Zuruecksetzen wird Open mit Mode => In_File benutzt. Die Funktion Aktuelles_Element kann nur kombiniert mit Naechstes_Element und mit Hilfe von Read aufgerufen werden (d.h. mit dem Lesen erfolgt gleich das Weiterschreiten). Dies bedeutet, daß in Sequential_IO die strenge Trennung von Informatoren und Mutatoren nicht erfolgt, ähnlich wie dies häufig auch bei Stapelimplementierungen der Fall ist.

Die Ausnahme EDateiende heißt hier End_Error, die logische Funktion Ende_der_Datei auf englisch End_of_File. Von daher ist die Funktionalität und die Schnittstelle der beiden Pakete fast identisch. Der wesentliche Unterschied erscheint im Parameter Name von Open: Der Vorbesetzungswert "" (leere Zeichenkette) ergibt eine temporäre Datei (praktisch identisch mit der aus GSeq_Datei). Wenn aber hier der Name einer externen Datei z.B. auf der Festplatte angegeben wird, werden alle Eintragungen hierauf übertragen. Bei einem nächsten Programmlauf kann eine externe Datei mit demselben Namen zum Lesen geöffnet und die früher geschriebenen Daten gelesen werden. Diese Fähigkeit der Datentypschablone File_Type ermöglicht Persistenz für seine Objekte.

Vor der Benutzung muß diese Paketschablone für den Basistyp ausgeprägt werden:

with Sequential_IO;
	...
	type TBasis is ... 
	package MBasis_IO is new Sequential_IO (Element_Type => TBasis);
	Eine_Datei, Andere_Datei: MBasis_IO.File_Type;
	Puffer: TBasis;
begin
	MBasis_IO.Open (File => Eine_Datei, Mode => MBasis_IO.In_File, Name => "EINGABE.DAT");
	MBasis_IO.Open (File => Andere_Datei, Name => "AUSGABE.DAT", Mode => MBasis_IO.Out_File);
		...
	MBasis_IO.Read (File => Eine_Datei, Item => Puffer);
	MBasis_IO.Write (File => Andere_Datei, Item => Puffer);
		 ...

Da der Dateityp eingeschränkt privat ist, sind die Objekte untereinander nicht kompatibel. Es ist also nicht möglich, den Inhalt zweier Dateien etwa durch den Operatoraufruf Eine_Datei = Andere_Datei miteinander zu vergleichen.

Die Operationen von Sequential_IO können folgende Ausnahmen auslösen:

Übung: Entwickeln Sie ein Programm, das die Operationen der Standard-Dateischablone (Open, Read, Write) menügesteuert ausführt. Testen Sie es mit Ihrem Lieblingdatentyp als Ausprägungsparameter. Fangen Sie die auftretenden Ausnahmen auf und reagieren Sie angemessen.


© Prof. Dr. Andreas Solymosi

Rückmeldungen bitte an den Autor solymosi@tfh-berlin.de

Leitseite des Autors