Dateiverwaltung mit Sequentiellen und Direktzugriffsdateien

In diesem Lehrgang sollen die Grundbegriffe der Dateiverwaltung kurz erklärt und anhand von BASIC-Beispielen aufgezeigt werden. Es werden die Methoden des sequentiellen und des direkten Zugriffs sowie deren Kombination besprochen. Dadurch soll ein Grundstock geschaffen werden, aus dem man dann ohne weiteres eigene Dateien erstellen kann. Natürlich gehören zu einem Dateiverwaltungsprogramm noch mehr als bloße Ein- und Ausleseroutinen. Doch das übrige wie zum Beispiel irgendwelche Sortieroder Formatierroutinen muß man sich selbst nach eigenen Wünschen gestalten, während die Dateizugriffsmethoden die gleichen bleiben.

Sequentielle Dateien

Sequentielle Dateien haben den Vorteil, daß sie leichter zu erstellen sind als Direktzugriffsdateien, sind aber bei der Verarbeitung der Daten in ihrer Geschwindigkeit und Flexibilität sehr begrenzt. Sie heißt deswegen sequentielle Datei, weil alle Daten nacheinander in ihrer Reihenfolge geschrieben werden. In der gleichen Art und Weise werden sie auch wieder zurückgelesen. Die maximale Menge der Daten ist von der Größe des verfügbaren Arbeitsspeichers abhängig, da man nicht auf einzelne Datensätze auf der Diskette zurückgreifen kann. Somit muß man jedesmal die ganze Datei in vollem Umfang laden, um die Daten zu verarbeiten. Ein Nachteil ist auch, daß man beim ATARI ST BASIC keine Möglichkeit hat, einfach Daten direkt auf Diskette an eine schon bestehende sequentielle Datei anzuhängen. Andere BASIC-Interpreter haben für diesen Fall den APPEND-Befehl. Es bleibt also nur die Möglichkeit seine Datei in den Speicher zu laden, dort die neuen Daten anzuhängen und wieder zurückzuspeichern.

Die folgenden Funktionen und Anweisungen werden in Verbindung mit sequentiellen Dateien benutzt:

Der CLOSE-Befehl

Sobald der Befehl CLOSE ausgeführt wird, wird die Beziehung zwischen der Datei und dem Ein- bzw. Ausgabegerät aufgehoben. Eine nachfolgende Ein- oder Ausgabeoperation, die dieselbe Dateinummer anspricht, ist ohne ein erneutes öffnen der Datei mit derselben Dateinummer nicht mehr gültig. Man kann dieselbe Dateinummer dann aber auch für eine andere Datei benutzen. Durch das Schließen der Datei werden Daten, die noch in einem Zwischenspeicher (Puffer) stehen, auf Diskette abgespeichert. Wenn man CLOSE ohne eine nachfolgende Dateinummer angibt, werden alle derzeit geöffneten Dateien geschlossen.

Der EOF-Befehl

Mit dem EOF-Befehl (End of File) wird das Dateiende einer sequentiellen Datei abgefragt und somit eine Fehlermeldung vermieden.

Der INPUT#-Befehl

Der INPUT#-Befehl liest Daten aus einer Diskettendatei und weist sie Programmvariablen zu. Zuvor muß zuerst die Datei mittels des OPEN-Befehls geöffnet werden. Die Daten müssen so aussehen, als wären sie als eine normale Antwort auf den INPUT-Befehl gegeben worden. Daraus folgt, daß bei numerischen Werten führende Leerstellen, Zeichen für Wagenrücklauf oder Zeilenvorschub ignoriert werden. Es sei denn, sie stehen hinter dem numerischen Wert, wobei sie dann aber nur als Ende des Wertes erkannt werden.

Der INPUT$-Befehl

Für Datenfernverarbeitungsdateien ist dieser Befehl am günstigsten zu benutzen, da er im Gegensatz zu INPUT# und LINE INPUT# alle ASCII-Zeichen aus einer Datei einliest.

Der LINE INPUT#-Befehl

Dieser Befehl liest alle Zeichen einer Datei bis zum Zeichen für Wagenrücklauf bzw. Zeilenvorschub. Zuvor muß zuerst die Datei mittels des OPEN-Befehls geöffnet werden. Alle Daten einschließlich irgendwelcher Trennzeichen werden dabei in eine Zeichenkette übernommen. Die maximale Anzahl der übernommenen Zeichen beträgt 254. Der LINE INPUT#-Befehl ist zum Beispiel zu benutzen, wenn man ein BASIC-Pro-gramm in Datenform in ein anderes Programm einlesen will.

Der LOC-Befehl

Bei sequentiellen Dateien übergibt LOC die Anzahl der gelesenen Datensätze der Datei seit sie eröffnet wurde. Sobald eine sequentielle Datei für eine Eingabe eröffnet wird, liest das BASIC den ersten Sektor der Datei, so daß LOC schon eine 1 übergibt, bevor irgendeine Eingabe von der Datei gelesen wurde. Bei Dateien mit Direktzugriff übergibt LOC die Datensatznummer des letzten gelesenen oder geschriebenen Datensatzes.

Der LOF-Befehl

Dieser Befehl übergibt die aktuelle Anzahl der Bytes, die der Datei zugeordnet sind (Länge der Datei).

Der OPEN-Befehl

Mit diesem Befehl wird die Ein- bzw. Ausgabe von und zu einer Datei geöffnet. Es sind dabei drei Modi möglich:

”O” steht für Ausgabe (Output) an eine sequentielle Datei.

”1” steht für Eingabe (Input) an eine sequentielle Datei.

”R” steht für Ein- und Ausgabe von bzw. an eine Direktzugriffsdatei.

Der OPEN-Befehl muß vor allen Ein-und Ausgabebefehlen einer Datei stehen. Es ist jederzeit möglich mehrere Ein- und Ausgabedateien auf einmal zu öffnen.

Der PRINT#-Befehl

Mit dem PRINT#-Befehl werden die Daten in die Datei geschrieben. Dabei entspricht das Format der Daten genau dem Format des PRINT-Befehls, der Daten auf den Bildschirm schreibt. Eine Liste von Ausdrücken muß also mit Semikolon getrennt werden oder mit Kommata falls man Leerstellen zwischen den Daten wünscht.

Der PRINT# USING-Befehl

Er entspricht weitgehend dem PRINT#-Befehl, mit dem einzigen Unterschied, daß die Daten formatiert ausgegeben werden können.

Der WRITE#-Befehl

Dieser Befehl schreibt ebenfalls Daten in eine sequentielle Datei. Der Unterschied zu PRINT# besteht darin, daß WRITE# Kommata zwischen die Angaben einfügt, wie sie geschrieben werden und Zeichenketten (Strings) in Anführungszeichen einschließt. Aus diesem Grund braucht der Anwender keine Trennzeichen in diese Liste einzufügen. Außerdem werden auch keine Leerstellen vor eine positive Zahl gesetzt. Ein Wagenrücklauf bzw. Zeilenvorschub wird nach der letzten Angabe der Liste geschrieben.

Aufbau und Zugriff auf eine sequentielle Datei

Nachdem nun ausführlich die Befehle besprochen wurden, die für eine sequentielle Datei notwendig sind, soll jetzt gezeigt werden, wie man eine solche Datei aufbaut. Dazu sind folgende Schritte nötig:

  1. Die Datei muß mittels des OPEN-Befehls in dem Modus ”0” für eine Ausgabe geöffnet werden.
  2. Die Daten müssen mittels der Anweisungen PRINT#, WRITE# oder PRINT# USING in die Datei geschrieben werden.
  3. Soll nun auf die Daten zugegriffen werden, muß man zuerst die Datei mit dem CLOSE-Befehl schließen und anschließend mit dem OPEN-Befehl in dem Modus „I“ für Eingabe geöffnet werden.
  4. Jetzt kann man mit den Anweisungen INPUT# oder LINE INPUT# Daten aus der sequentiellen Datei lesen.

In Listing 1 wird dazu ein kurzes Beispiel gegeben.

Direktzugriffsdateien

Direktzugriffsdateien sind Dateien mit wahlfreiem Zugriff, d. h. man kann auf jeden einzelnen Datensatz auf der Diskette zugreifen und diesen anschließend bearbeiten. Um dies zu ermöglichen, ist jedem dieser Datensätze eine sogenannte Datensatznummer zugeordnet. Für Erstellung von Direktzugriffsdateien sind allerdings mehr Programmschritte erforderlich als für eine sequentielle Datei, was somit einen größeren Programmieraufwand bedeutet. Außer dem Vorteil des wahlfreien Zugriffs kann man normalerweise mit Direktzugriffsdateien auch Platz auf der Diskette sparen. Das rührt daher, daß nicht, wie bei sequentiellen Dateien im ASCII-Format abgespeichert wird, sondern im Binär-Format. Aus diesem Grund kann man zum Beispiel im BASIC Maschinenprogramme in hexadezimaler Form in DATA-Zeilen erstellen, anschließend über den READ-Befehl in ein Variablenfeld einiesen und dann als lauffähiges Maschinenprogramm mit der OPEN-Anweisung in dem Modus „R" auf Diskette abspeichern. Dieses „R" werden vielleicht noch viele von Commodores C 64 her kennen, denn es ist das Kürzel für relativ und relative Dateien und Direktzugriffsdateien sind genau dasselbe. Zu den Datensätzen wäre noch zu sagen, daß sie eine Länge bis zu 32 767 Bytes haben können, also nicht von der Größe eines Sektors auf der Diskette (512 Bytes) abhängig sind. Daraus ergibt sich, daß ein Teil des Datensatzes in dem einen und der andere Teil in dem anderen Sektor stehen kann.

Neben den Befehlen CLOSE, OPEN, LOC und LOF, die ja bereits oben ausführlich beschrieben wurden, werden folgende Befehle bei Direktzugriffsdateien benutzt:

Die CVD-, CVI- und DVS-Befehle Numerische Werte, die aus einer Direktzugriffsdatei gelesen wurden, müssen von Zeichenketten in Zahlen umgewandelt werden. Dies geschieht mit den CV-Befehlen. Dabei wandelt CVD eine acht Bytes umfassende Zeichenkette in eine Zahl doppelter Genauigkeit um. CVS wandelt eine Vier-Byte-Zeichenkette in eine Zahl einfacher Genauigkeit um und CVI wandelt eine Zwei-Byte-Zeichenkette in eine Integerzahl um. Die Funktionen CVD, CVI und CVS ändern dabei nicht die Bytes der aktuellen Daten, sondern sie ändern nur den Weg, wie BASIC diese interpretiert.

Der FIELD-Befehl

Mit dem FIELD-Befehl legt man Platz für Variablen in einem Puffer für Direktzugriffsdateien an. Dabei liest FIELD aber keine Daten in den Puffer. Man kann diesen Befehl also in etwa mit dem DIM-Befehl vergleichen, der ja auch nur Platz für Datenfelder schafft. Zu beachten ist noch, daß die Gesamtzahl der Bytes, die der FIELD-Anweisung zugeordnet werden, nicht die Datensatzlänge überschreitet, die zuvor in der OPEN-Anweisung angegeben wurde, da man ansonsten eine Fehlermeldung bekommt.

Der GET-Befehl

Mit dem GET-Befehl liest man einen Datensatz aus einer Direktzugriffsdatei in einen Puffer. Da das BASIC und das TOS so viel wie möglich Datensätze in dem Puffer für Direktzugriffsdateien Zwischenspeichern, liest die GET-Anweisung nicht unbedingt bei jedem Zugriff von Diskette, da die Daten auch schon im Puffer stehen können.

Der LSET- und der RSET-Befehl

Mit diesen Befehlen werden die Daten in den Datenpuffer für Direktzugriffsdateien geschrieben. Hinter ihnen muß eine Zeichenkettenvariable stehen, die zuvor in der FIELD-Anwei-sung definiert wurde. Wenn diese Variable weniger Bytes benötigt als in der FIELD-Anweisung angegeben, werden die restlichen Bytes mit Leerstellen aufgefüllt. Bei LSET wird die Variable linksbündig und bei RSET rechtsbündig abgespeichert. Falls die Variable mehr Bytes hat als zuvor definiert, werden die Zeichen rechtsbündig abgeschnitten. Numerische Werte müssen zuvor in Zeichenketten umgewandelt werden (Siehe nächsten Befehl).

Die MKD$-, MKI$- und MKS$-Befehle Jeder numerische Wert, der mit den LET- und RSET-Befehlen in den Puffer für Direktzugriffsdateien gebracht wird, muß zuvor in eine Zeichenkette umgewandelt werden. Dies geschieht durch die Befehle MKD$, MKI$ und MKS$. Sie bewirken das genaue Gegenteil der CVD-, CVI- und CVS-Befehle. Die MK-Befehle unterscheiden sich von dem STR$-Befehl dadurch, daß sie nicht die Datenbytes ändern und BASIC sie anders interpretiert.

Der PUT-Befehl

Mit dem PUT-Befehl werden die Daten aus dem Puffer für Direktzugriffsdateien auf Diskette geschrieben. Zuvor müssen die Daten mit PRINT #, PRINT# USING, WRITE#, LSET oder RSET in den Puffer gebracht werden. Im Falle von WRITE # wird der Puffer vom BASIC mit Leerstellen bis zum Wagenrücklauf bzw. Zeilenvorschub aufgefüllt. Jeder Versuch über das Ende des Puffers hinauszuschreiben oder zu lesen, ergibt eine Fehlermeldung. Wie beim GET-Befehl werden die Daten solange im Puffer zwischengespeichert bis er voll ist und erst dann auf Diskette geschrieben. Bei einem Reset würden also alle Daten, die im Puffer stehen, verloren gehen.

Erstellen einer Direktzugriffsdatei

Wie bei der sequentiellen Datei soll nun in der folgenden Anleitung gezeigt werden, wie man eine Direktzugriffsdatei erstellt.

  1. Zuerst muß die Datei mit dem OPEN-Befehl in dem Modus „R" geöffnet werden. Dabei muß die Datensatzlänge in dem OPEN-Be-fehl bestimmt werden. Wird die Datensatzlänge weggelassen, wird als Standardlänge 128 Bytes angenommen.
  2. Mit dem FIELD-Befehl wird dem Puffer Platz für die Variablen zugeteilt, die auf Diskette geschrieben werden sollen.
  3. Mit LSET oder RSET werden die Daten in den Puffer gebracht. Zuvor müssen die Daten mittels der MK-Befehle in Zeichenketten umgewandelt werden.
  4. Die Daten werden nun mit dem PUT-Befehl auf die Diskette geschrieben.

Zugriff auf eine Direktzugriffsdatei

Wenn man sich jetzt eine Direktzugriffsdatei erstellt hat, will man die Daten ja auch wieder in den Speicher lesen können. Dazu gibt die folgende Anleitung die Möglichkeit.

  1. Die Datei muß zuerst einmal wieder mit dem OPEN-Befehl im Modus „R" geöffnet werden.
  2. Ebenfalls muß der FIELD-Befehl wieder für Platz im Puffer sorgen. Allerdings braucht man, falls man in demselben Programm ein- und ausliest, den OPEN- und den FIELD-Befehl nur einmal benutzen. Die Datei muß also nicht wie bei der sequentiellen Datei zuerst geschlossen und dann wieder geöffnet werden.
  3. Mit der GET-Anweisung wird nun der gewünschte Datensatz von der Diskette in den Puffer gelesen.
  4. Die Daten können jetzt vom Programm verarbeitet werden. Numerische Werte müssen allerdings, wie schon mehrfach erwähnt, zuerst mit den CV-Befehlen umgewandelt werden.

Listing 2 und 3 geben ein Beispiel für Direktzugriffsdateien

Die Kombination aus Sequentieller-und Direktzugriffsdatei

Eine einfache und überaus sinnvolle Methode Dateien zu verwalten besteht darin, die beiden bisher kennengelernten Dateienarten miteinander zu koppeln. Da man davon ausgehen kann, daß Daten so gut wie immer im Arbeitsspeicher sortiert werden, ist somit zweckmäßig alle Daten, die sortiert werden sollen, in eben diesen Speicher einzulesen. Dafür bietet sich eine sequentielle Datei an. In dieser sequentiellen Datei muß nun neben den betreffenden Daten auch noch die zu den Daten passende Datensatznummer stehen. In der dazugehörigen Direktzugriffsdatei befinden sich, nach Datensätzen geordnet, die übrigen Daten, die nicht sortiert werden sollen.

Wenn man nun zum Beispiel nach einem bestimmten Datensatz sucht, dann lädt man die sequentielle Datei in ein Datenfeld im Speicher. Dort läßt man jetzt nach dem ausgewählten Kriterium suchen. Nachdem man es gefunden hat, spaltet man mit einem Stringbefehl (LEFTS, RIGHTS oder MID$) von dem sequentiellen Datensatz die Datensatznummer für die Direktzugriffsdatei ab. Damit hat man jetzt die Möglichkeit die restlichen Daten aus der Direktzugriffsdatei nachzuladen. Man spart somit eine Menge Platz im Arbeitsspeicher, da der vollständige Datensatz in zwei Teile gespalten worden ist und nur ein Teil davon sich im Speicher befindet. Diese Methode hat aber auch wiederum den Nachteil, daß man nur nach bestimmten Daten suchen kann, nicht nach allen.

Ein Beispiel für diese Art von Datenverarbeitung kann man an Listing 4 sehen.

List of \SEQDATEI.BAS

10	'-------Sequentielle Datei -------
20	'
30	closew 3:clearw 2:fullw 2
40	dim ns$(20),nl$(20)
50	schreiben:
60	x = 0
70	open "O",#1,"Daten.seq"
80	gotoxy 1,1:print
90	input "Name : ";ns$(x)
100	if ns6(x)="*" then goto 140
110	x=x+1
120	print
130	goto 90
140	print
150 print "Bitte Datendiskette einlegen und Taste druecken !" 
160	a=inp(2)
170	for i=0 to x-1
180	print#1,ns$(i)
190	next i
200	close 1:print
210 '
220 '--------------------------------------------------------
230	'
240	1esen:
250	x=0
260	open "I",#1,"Daten.seq"
270 if eof(1) then close 1 :end 
280	input#1,nl$(x)
290	print
300 print "gelesener Name : “;nl$(x)
310	x=x+1
320	goto 270

Listing 1: Beispiel für eine sequentielle Datei

List of \DZGOUT.BAS

10	'------- Erstellen einer Direktzugriffsdatei ------
20	'
30 closew 3:clearw 2:fullw 2 
40	dnr=1
50 open "R",#1,"Daten.dzg",24 
60	field 1,20 as n$,4 as g$
70	gotoxy 1,1:print
80 print "Bitte Datendiskette einlegen und Taste drücken !" 
90	a=inp(2)
100	print
110 input "Name : ";ns$
120 if ns$="*" then close 1:end 
130	input "Geburtsjahr : ";g
140	lset n$=ns$
150	lset g$=mki$(g>
160	put #1,dnr
170	dnr=dnr+l
180	print
190	goto 110

Listing 2: Erstellen einer Direktzugriffsdatei

List of \DZGIN.BAS

'------- Zugriff auf eine Direktzugriffsdatei --------
'
30 closew 3:clearw 2:fullw 2 
40 open "R",#1,"Daten.dzg",24 
50 field 1,20 as n$,4 as g$ 
60 gotoxy 1,1:print
70 print "Bitte Datendiskette einlegen und Taste drücken !"
80 a= inp(2)
90 print
100 input "Datensatznummer (99=Ende) : ";dnr
110 if dnr=99 then close 1:end
120 get #1,dnr
130 print "Name : ";n$
140 print "Geburtsjahr : ";cvi(g$)
150 goto 90

Listing 3: Lesen einer Direktzugriffsdatei

List of \KOMBIDRT.BAS

10	'-----Kombinierte Datei----------
20	'
30	closew 3:clearw 2:fullw	2
40	dim ns$(20),nl$(20)
50	schreiben:
60	x=1
70	open "O",#1,"Seqkombi.dat"
80 	open "R",#2,"Dzgkombi.dat",16 
90	field 2,4 as g$,12 as tS
100	gotoxy 1,1:print
110 	input "Name (max.8 Zeichen) : ";nsS(x)
120 	if ns$(x)="*" then goto 210
130 	if len(ns$(x))<8 then ns$(x)=nsS(x)+" ":goto 130 
140	ns$(x)=ns$(x)+str$(x)
150	input "Geburtsjahr : ";g
160 	input "Tel.-Nr. (max*. 12 Zeichen) : ";tel$
170	gosub direktschreiben
180	x=x+1
190	print
200	goto 110
210	print
220 print "Bitte Datendiskette einlegen und Taste drücken !" 
230	a=inp(2)
240	for i=1 to x-1
250	print #1,ns$(i)
260	next i
270 close 1:print 
280 goto lesen 
290 '
300	direktschreiben:
310 lset g$=mki$(g)
320 lset t$=tel$
330	put #2,x
340	return
350	'
360	' -------------------------------------------
370	'
380	lesen:
390	x=1
400	open "I",#1,"Seqkombi.dat"
410 if eof(1) then close 1:goto 460 
420 input#1,nl$(x)
430 x=x+1 
440 goto 410 
450 print 
460 suchen:
470 input "Welchen Namen wollen Sie lesen :	";nl$
480 if len(nl$)<8 then nl$=nl$+" ":goto 480
490 for i = 1 to x-1
500	nv$(i)=left$(nl$(i),8)
510 if nl$=nv$(i) then goto 550 
520 next i
530 print "Name nicht vorhanden !"
540 goto 460
550	gosub direktlesen
560 ausgeben:
570	print "Name : ";nl$
580	print "Geburtsjahr : ";cvi(g$)
590	print "Tel.-Nr.: ";tS
600 print
610	input "Noch einen Datensatz (j/n) ";jn$
620 if jn$="j" then goto suchen
630 if jn$="n" then close:end
640 if jn$<>"j" or jn$<>"n" then goto 610
650 direktlesen:
660	dnr=val(right$(nl$(i),2))
670 get #2,dnr 
680 return

Listing 4: Kombinierte Datenverarbeitung



Aus: ST-Computer 02 / 1986, Seite 27

Links

Copyright-Bestimmungen: siehe Über diese Seite