← ST-Computer 07 / 1986

EinfĂŒhrung in die Programmiersprache Pascal Teil 6

Grundlagen

Wie schon im letzten Teil erwÀhnt, geht es diesmal um den letzten strukturierten Datentyp FILE. Damit ist es möglich, sequentielle Dateien zu erstellen und abzuspeichern. Danach kann man die Daten wieder einlesen und auf dem Bildschirm oder dem Drucker ausgeben.

Anmerkung: Die in diesem Teil des Kurses vorkommenden Programme sind lĂ€nger als die in den vorangegangenen Teilen. Dies ist jedoch notwendig, wenn man den Umgang mit Dateien an einem sinnvollen Beispiel aufzeigen will. Das Programm SEQ_DAT soll als GerĂŒst fĂŒr Ihre eigene Datei dienen, denn es kann durch seinen modularen Aufbau leicht den eigenen Vorstellungen angepaßt und erweitert werden.

Sequentielle Dateien

Eine Datei (FILE) ist eine Folge von Komponenten des gleichen Datentyps. Die Variablenvereinbarung hat die Form: /

Dateivariable : FILE of Datentyp;

Als Datentyp sind prinzipiell alle in Pascal definierten Datentypen erlaubt.

.datei: file of integer;

Damit wird eine Datei definiert, die nur Komponenten vom Typ INTEGER enthalten darf. Der Zugriff auf die einzelnen Komponenten der Datei ist zunĂ€chst nur sequentiell möglich. Dies bedeutet, daß man nicht ein beliebiges Element der Datei ansprechen kann, wie dies zum Beispiel bei einem ARRAY möglich ist, indem man den Index angibt. Will man bei einer sequentiellen Datei auf ein bestimmtes Element zugreifen, so muß man alle Komponenten bis zum gewĂŒnschten Element einlesen.

Wenn, wie oben beschrieben, ein FILE deklariert wurde, entsteht gleichzeitig eine Zeiger- bzw. Puffervariable:

datei^

Diese Zeigervariable ist kein Teil des FILES. Sie zeigt lediglich auf das aktuelle Element der Datei. Nur mit ihr ist ĂŒberhaupt ein Zugriff auf die Datei möglich.

Nach der Deklaration enthĂ€lt eine Datei nur das Zeichen EOF (end of file), also das Dateiendezeichen. Um etwas hineinschreiben zu können, muß man die Datei öffnen:

rewrite(datei,"beispiel")

Wenn nun in eine Datei etwas geschrieben werden soll, dann weist man der Zei^ervariablen einen Wert zu

datei^: = 7

und schreibt diesen mit dem Befehl

put(datei)

in die Datei.

Um Daten aus einer Datei lesen zu können, muß man sie mit dem Befehl

reset(datei,"beispiel")

öffnen. Die Puffervariable datei' hat dann sofort den Wert des ersten Elements der Datei ’beispiel’. Der Wert kann dann auf dem Bildschirm ausgegeben werden:

write(datei^)

Um das nĂ€chste Element der Datei ausgeben zu können, muß die Puffervariable um eine Stelle verschoben werden. Dies geschieht mit dem Befehl:

put(datei)

Der jetzige Wert der Puffervariablen kann nun wie oben ausgegeben werden.

Bei den meisten PASCAL-Compilern kann die Anweisungsfolge

datei^ := x; put(datei)

bzw.

x: = datei^; get(datei)

ersetzt werden durch

write(datei,x)

bzw.

read(datei,x)

Der Filetyp TEXT

Es gibt einen vordefinierten FILE-Datentyp TEXT. Er ist definiert als:

TEXT : file of char

Textdateien können gegenĂŒber einem FILE OF CHAR mit den Standardprozeduren readln, writeln und eoln bearbeitet werden. Daran lĂ€ĂŸt sich bereits erkennen, daß dieser Filetyp in Zeilen aufgeteilt ist und deshalb die entsprechenden Befehle auf ihn anwendbar sind.

Textdateien dĂŒrfen nur aus den Typen CHAR, INTEGER und REAL bestehen. Die Möglichkeiten zur Bearbeitung von Textdateien sind stark implementierungsabhĂ€ngig, man sollte deshalb immer das Handbuch zu Rate ziehen, wenn man sich tiefer damit beschĂ€ftigen will.

Das Zeilenende wird mit EOLN gekennzeichnet. Wenn EOLN ’true’ wird, dann enthĂ€lt die Puffervariable ein Leerzeichen.

READLN(datei,Variablenliste) kann so viele Zeichen einiesen, bis das Zeilenende erreicht ist. Die Puffervariable zeigt danach auf die nÀchste Zeile.

Mit WRITELN(datei,Variablenliste) kann die ganze Variablenliste geschrieben werden. Danach wird ein (systemspezifisch) Zeilenendezeichen EOLN gesetzt.

Programmbeschreibung

Das Programm SEQ__DAT (Listing 1) dient zum Schreiben und Lesen von sequentiellen Dateien. Es besteht im wesentlichen aus vier Prozeduren die vom Hauptprogramm aufgerufen werden. Bei der Variablendeklaration wird eine Variable ’datei’ vom Typ FILE of String festgelegt. Außerdem wird ein Feld eingerichtet, das momentan nur 51 Elemente aufnehmen kann.

Anmerkung: Die Prozedur CLRSCR ist in Turbo-Pascal vorhanden, fehlt jedoch im CCD PASCAL von ATARI.

Die nĂ€chste Prozedur ERSTELLEN dient dem Erstellen einer Datei. Sie gibt die Anzahl der geschriebenen Daten in der Variablen nr an das Hauptprogramm zurĂŒck.

Es muß zunĂ€chst der Name eingegeben werden, unter dem die Datei auf der Diskette abgelegt werden soll. Dieser kann maximal 12 Zeichen enthalten.

Danach folgt die Eröffnung der Datei:

rewrite (datei,dateiname);

Eine Datei gleichen Namens wird dabei unweigerlich gelöscht(!).

Ein Wert wird in die Puffervariable datei^ eingelesen und in die Datei geschrieben, dies geschieht mit den Befehlen:

readln(datei^); put(datei);

Die Einleseschleife lĂ€uft so lange, bis ’★ ’ eingegeben wird. In der Variablen nr ist dann die Anzahl der eingelesenen Daten enthalten.

Die Prozedur LESEN erwartet als erstes den Dateinamen, die entsprechende Datei wird dann geöffnet:

reset(datei,dateiname);

Die erste Abfrage

if not eof(datei) then...

stellt fest, ob es eine Datei mit dem eingegebenen Namen gibt, und beginnt dann mit dem Einlesen. Hierzu wird eine Schleife verwendet, die so lange lÀuft, bis das Datenende gefunden wird.

Die eingelesenen Daten werden in ein Feld geschrieben und sind danach immer verfĂŒgbar.

Die Prozedur LESEN ĂŒbergibt die Anzahl der gelesenen Elemente an das Hauptprogr am m.

Die Prozedur AUSGABE benötigt vom Hauptprogramm die Anzahl der Elemente. Sie gibt dann das Datenfeld auf dem Bildschirm aus.

Die letzte Prozedur DRUCKEN gibt die Werte auf dem Drucker aus. Dazu wird mit

rewrite(output,’prn:’)

die Standardfunktion OUTPUT auf den Drucker gelenkt. Dies bedeutet, daß ab dieser Anweisung alle Befehle, die sich sonst auf den Bildschirm beziehen, auf den Drucker ausgegeben werden.

Die Prozedur ruft deshalb nun die Prozedur AUSGABE auf. Auf dem Drucker erscheint dadurch das gleiche Bild wie zuvor auf dem Monitor.

Um die Druckerausgabe zu beenden, lenkt man die Ausgabe wieder auf den Bildschirm

rewrite(output,'con:')

(Diese Befehle gelten in dieser Form fĂŒr CCD-PASCAL. Bei anderen Compilern muß wegen der abweichenden Syntax im Handbuch nachgeschlagen werden.)

Das Hauptprogramm enthĂ€lt nur das MenĂŒ zur Steuerung der einzelnen Funktionen. Die Funktionen werden mit einem entsprechenden Buchstaben angewĂ€hlt. Mit einer CASE-Anweisung werden dann die einzelnen Prozeduren aufgerufen und die Parameter ĂŒbergeben.

Direktzugriffsdatei (random-access)

Die Datei kann bis jetzt nur beschrieben und gelesen werden, ein HinzufĂŒgen oder Ändern von Daten ist nicht vorgesehen. Dies ist zwar prinzipiell auch mit einer sequentiellen Datei möglich, aber sehr umstĂ€ndlich. Die elegantere Methode fĂŒr diese Aufgaben ist der direkte Zugriff auf eine Datei. Beim CCD-PASCAL ist wahlweise sequentieller und direkter Zugriff möglich, die beiden Formen können sogar gemischt werden. Direktzugriffsdateien sind in STANDARD-PASCAL nicht definiert, jedoch in verschiedenen Pascal-Dialekten bereits vorgesehen (z. B. TURBO-und UCSD-PASCAL). Dabei ist die Syntax der speziellen Befehle z. T. sehr verschieden (bei den oben angegebenen Dialekten wird dafĂŒr die Funktion SEEK definiert, die die Puffervariable auf ein beliebiges Element der Datei setzt). Beim CCD-PASCAL ist der Direktzugriff besonders einfach.

Eine Datei besteht aus einer Folge von Elementen, die hintereinander angereiht sind. Durch Angabe der Nummer kann ein Element direkt angesprochen werden. GegenĂŒber der sequentiellen Methode kommt bei der Syntax lediglich die Angabe der Nummer des Elements hinzu. Geöffnet wird eine Direktzugriffsdatei genauso wie eine sequentielle, mit rewrite bzw. reset. Das Schreiben einer Datei hat dann die Form:

variable^:=x put(variable,nummer)

Gelesen wird die Datei mit:

get(variable,nummer) x:=variable^

Programmodule

Das Programm SEQ_DAT kann mit den Prozeduren VERÄNDERN (List ing 2) und ERWEITERN (Listing 3) ergĂ€nzt werden. Diese Prozeduren arbeiten mit direktem Zugriff auf Dateien. Um die Module vom Hauptprogramm aus nutzen zu können, muß, nach ihrem EinfĂŒgen im Vereinbarungsteil, der MenĂŒteil erweitert werden. Dies kann dann wie in Listing 4 dargestellt aussehen.

Anmerkung: die Prozedur ERWEITERN muß im Vereinbarungsteil hinter der Prozedur LESEN stehen, weil sie diese auf ruft.

procedure VERAENDERN: var nr : integer; begin clrscr; writeln('VERAENDERN einer Datei'): writeln; write ('Dateiname'); readln(dateiname) ; writeln; reset(datei,dateiname); if not eof(datei) then repeat write ('Nummer des Datensatzes: '); readln(nr); write ('Inhalt des Datensatzes'); get(datei,nr); writeln(datei^); write('neuer Inhalt : ');readln(datei^); if datei^<>'*' then put (datei, nr); until datei^='*' else begin write('Datei existiert nicht !'); read(ch) end end; { VERAENDERN }

Listing 2

Struktogramm zu Listing 2
procedure ERWEITERN; var nr : integer; begin clrscr; writeln('ERWEITERN einer Datei'); writeln; lesen(nr); { ermittelt Anzahl der Daten } reset(datei,dateiname); writeln; if not eof(datei) then repeat write('Datensatz-Nr ',nr,': '); readln(datei^); put(datei,nr); nr:=nr+1; until datei^='*' end; { ERWEITERN } Unterprogramm ERWEITERN Variable: nr LESEN(nr) Eingabe: dateiname Eröffnen der Datei Eingabe: datei^ PUT(datei,nr) nr : =nr + 1 wiederhole bis datei^='*'

Struktogramm zu Listing 3

Die Prozedur VERÄNDERN öffnet die Datei und fragt dann nach der Nummer des Datensatzes, der verĂ€ndert werden soll. Danach zeigt sie den alten Inhalt an und erwartet die Eingabe des Benutzers. Wenn man ein ’★’ eingibt, wird die Prozedur verlassen.

ERWEITERN ist eine Prozedur, mit der man Daten an eine bestehende Datei anhĂ€ngen kann. Auch dies ist bei sequentiellen Dateien meistens nicht möglich. Die Prozedur ruft ihrerseits die Prozedur LESEN auf, um die Anzahl der Daten der bereits existierenden Datei zu ermitteln. Danach wird die erste freie Datensatznummer ausgegeben; und das Programm wartet dann auf die Eingabe. Zum Verlassen der Prozedur gibt man ’★’ ein.

Ausklang

Mit diesem Teil ist der Kurs 'EinfĂŒhrung in die Programmiersprache PASCAL’ beendet. Es sind darin alle wesentlichen Befehle und Strukturen dieser Sprache besprochen worden. NatĂŒrlich sind die Möglichkeiten, die Pascal bietet, noch wesentlich vielfĂ€ltiger, sie können jedoch im Rahmen eines EinfĂŒhrungskurses nicht behandelt werden. Ich hoffe, daß Sie mit diesem Kurs soweit in die Sprache eingefĂŒhrt wurden, um selbst komplexe Programme entwicklen zu können. Sie sollten dabei, besonders bei lĂ€ngeren Programmen, nicht auf die Erstellung eines Struktogramms verzichten, denn diese Technik unterstĂŒtzt die strukturierte, gut lesbare Programmierung erheblich.

Viel Spaß beim weiteren Programmieren.

(MN)

writeln('Sequentielle & Random-Access DATEI'); writeln; . . writeln('Datei Er(W)eitern'); writeln; writeln(' Daten (V)eraendern') : . . case eingabe of . . 'V',’v' : veraendern; 'W',’w' : erweitern;

Listing 4

# BefehlsĂŒbersicht zur Behandlung von Dateien

Auf eine geöffnete Datei lĂ€ĂŸt sich folgende Funktion anwenden:

EOF(dateivariable): Diese Funktion liefert den Wert ’true’, wenn das Ende einer Datei erreicht wird, ansonsten den Wert ’false’.

Die folgende Prozeduren lassen sich auf Dateien anwenden:

REWRITE(dateivariable,dateiname): Mit diesem Befehl wird eine Datei ’datei-name’ zum Beschreiben geöffnet. Eine eventuell existierende Datei gleichen Namens wird dabei sofort gelöscht(l). Die Zeigervariable ist in diesem Moment noch nicht definiert, eof(variable) ist ’true’.

RESET(dateivariable,dateiname): Dieser Befehl öffnet eine Datei zum Lesen. Die Zeigervariable variable wird auf die erste Komponente des FILES gesetzt und erhÀlt deren Inhalt. Wenn die Datei noch nicht bestand, dann ist die Zeigervariable unbestimmt, eof(variable) erhÀlt den Wert true.

PUT(dateivariable): Durch diesen Befehl wird der aktuelle Wert der Zeigervariable an das momentane Ende des FILES angehÀngt.

PUT(dateivariable,nummer): Dieser Befehle schreibt den Wert der Zeigervariable in die Datei an die Stelle NUMMER.

GET(dateivariable): Die Zeigervariable wird auf die nĂ€chste Komponente gesetzt und erhĂ€lt deren Inhalt. Wenn diese Komponente nicht existiert, dann ist sie Undefiniert. Wenn es keine weitere Komponente in der Datei gibt, dann wird eof(variable) ’true’.

GET(dateivariable,nummer): Mit diesem Befehl kann ein Element aus der Datei gelesen werden, das an der Stelle NUMMER steht.

CLOSE(dateivariable): Diese Anweisung bewirkt das Schließen einer Datei. Sie ist beim CCD-Pascal jedoch nicht notwendig, weil die Datei automatisch beim Verlesen des Blocks, in dem sie definiert wurde, geschlossen wird.

ERASE(dateivariable): Hiermit kann eine Datei auf der Diskette gelöscht werden. Dieser Befehl ist nur in wenigen Pascal-Versionen implementiert (z. B. TURBO- und CCD-PASCAL)

RENAME(dateivariable1,dateivariable2): Mit dieser Anweisung erhĂ€lt die Datei 1 den Namen der Datei 2. Dazu mĂŒssen beide Dateien geöffnet sein. Danach wird die Datei 2 gelöscht. Dieser Befehl ist in TURBO- und CCD-PASCAL vorhanden.

SEEK(dateivariable,nummer): Damit kann die Puffervariable auf das Element der Datei gesetzt werden, wodurch ein direkter Zugriff auf ein beliebiges Element möglich ist. Der Befehl ist z. B. in TURBO-, UCSD- und CCD-PASCAL vorgesehen.

List of SEQDAT_4.PAS

program SEQ_DATEI(input,output.datei): { Lesen und Schreiben von sequentiellen Dateien } { * erstellt mit CCD-Pascal * } var datei : file of string(10); { nur 10 Zeichen } feld : array(0..50) of string(10); { nur 50 Elemente } eingabe,ch: char; dateiname : string(12); nr,nummer : integer; procedure CLRSCR; { loescht den Bildschirm } begin write(chr(27), 'E') end; procedure ERSTELLEN(var nr: integer): begin clrscr; writeln('ERSTELLEN einer Datei'); writeln; write('Dateiname : '): readln(dateiname); writeln; rewrite(datei,dateiname); { kann alte Datei loeschen } nr:=—1; repeat nr:=nr+1; write(nr+1,'. '); readln(datei^); { Zeiger- oder Puffervariable der Datei } put(datei); until datei^='*'; end; procedure LESEN(var anzahl; integer); var nr : integer; begin clrscr; writeln('LESEN einer Datei'); writeln; write('Dateiname : '); readln(dateiname); writeln; reset(datei,dateiname); write('LESEN:'); nr:=-1; if not eof(datei) then while not eof(datei) do begin nr:=nr+1; feld[nr]:=datei^; get(datei); write('.'); end else begin write('Datei existiert nicht !'); read(ch); anzahl:=0 end; anzahl:=nr end; procedure AUSGABE(anzah1; integer); var nr : integer; begin clrscr; writeln('AUSGABE'); writeln; writeln('DATEI: ',dateiname); writeln; for nr:=0 to anzahl do writeln(nr,'. ‘,feld[nr]) end; procedure DRUCKEN: begin rewrite(output, 'prn:'); { siehe Handbuch } ausgabe(nr); rewrite(output,'con:'); end; begin { Hauptprogramm } dateiname:='no name'; nr:=0; repeat clrscr; writeln; writeln: writeln('Sequentielle DATEI'); writeln; writeln('Name der Datei : ',dateiname); writeln('Anzahl der Daten : ',nr); writeln; writeln('Datei (E)rstellen'); writeln('Datei (L)esen'); writeln; writeln('Daten (A)usgeben'); writeln('Daten (D)rucken');writeln; writeln(' (*) —> ENDE');writeln; write('Eingabe : '); read(eingabe) case eingabe of 'A','a' : begin if nr>0 then ausgabe(nr) else write('keine Daten vorhanden !'); read(ch); end; 'D','d' : drucken; 'E','e' : erstellen(nr); 'L','l' : lesen(nr); end; { case } until eingabe='*' { Menueschleife } end.

Listing 1

Struktogramm zu Listing 1