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 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:
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.
Mit dem EOF-Befehl (End of File) wird das Dateiende einer sequentiellen Datei abgefragt und somit eine Fehlermeldung vermieden.
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.
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.
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.
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.
Dieser Befehl übergibt die aktuelle Anzahl der Bytes, die der Datei zugeordnet sind (Länge der Datei).
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.
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.
Er entspricht weitgehend dem PRINT#-Befehl, mit dem einzigen Unterschied, daß die Daten formatiert ausgegeben werden können.
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.
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:
In Listing 1 wird dazu ein kurzes Beispiel gegeben.
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.
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.
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.
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.
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.
Wie bei der sequentiellen Datei soll nun in der folgenden Anleitung gezeigt werden, wie man eine Direktzugriffsdatei erstellt.
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.
Listing 2 und 3 geben ein Beispiel für Direktzugriffsdateien
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