C 64/VC 20
Relative Datei

Daten im (relativen) Direktzugriff

In diesem Bericht werden die Unterschiede zwischen der relativen und der sequentiellen Datei aufgezeigt und anhand eines Beispielprogramms die Programmierung einer relativen Datei erklärt.

Kommen wir gleich zu den Vor- und Nachteilen. Der Hauptvorteil der relativen Datei ist der schnelle Zugriff auf Daten. Ein weiterer Vorteil ist, daß eigentlich nur wenig Speicherplatz des Computers notwendig ist, nämlich nur so viel, wie das Programm selbst benötigt. Deshalb ist eine sinnvolle Anwendung dieser Dateiform auch mit dem VC 20 in der Grundversion möglich. Allerdings ist ein Diskettenlaufwerk unbedingt notwendig. Die sequentielle Datei hingegen benötigt viel Speicherplatz im Computer, und ein schneller Zugriff auf Daten ist nicht einfach zu realisieren, zumindest muß die gesamte Datei erst in den Speicher des Computers geladen werden, bevor man sinnvoll mit ihr arbeiten kann. Jedoch ist nicht unbedingt ein Diskettenlaufwerk notwendig, die Datasette tut’s auch, wenn auch erheblich langsamer. Der Nachteil der relativen Datei ist die Art des Zugriffs auf bestimmte Daten. Er ist nämlich nur über die Datensatznummer möglich. Das heißt: Angenommen, wir haben uns eine Adressendatei aufgebaut und auch schon eine Anzahl Adressen eingegeben. Wenn wir uns jetzt die Adresse von Anton Huber ausgeben lassen wollen, so ist das nicht möglich, indem wir den Namen »Huber« eingeben und dann das Ergebnis, seine Adresse, auf einen Schlag vor uns haben. Dazu müßten wir seine Satznummer kennen. Vereinfacht kann man sagen, daß die Satznummer angibt, die wievielte Adresse gemeint ist. Satznummer 14 bedeutet also die 14. Adresse. Diese Adresse findet der Computer, weil er vom Dateianfang ausgeht, den er sich merkt, und dann 13 Datensätze überspringt (auf den nächsten Datensatz positioniert), um direkt den 14. Datensatz zu lesen (daher der Name relative Datei: relativ zum Dateianfang).

Dazu ist eine Voraussetzung notwendig: Die Datensätze müssen alle die gleiche Länge haben. Wie das realisiert wird, erkläre ich noch.

Um einer möglichen Frustration vorzubeugen, sei gesagt, daß auch eine direkte Suche über einen Namen möglich ist, indem man die Vorteile der relativen Datei mit der der sequentiellen Datei verknüpft.

Eine relative Datei besteht im Prinzip aus 3 Teilen:

1. Einrichten einer Datei
2. Speichern (Schreiben) eines Datensatzes
3. Lesen eines Datensatzes

Gehen wir diese Teile Schritt für Schritt durch. Anhand des Listings können Sie diese Schritte mitverfolgen.

1. Einrichten einer relativen Datei

Um mit einer relativen Datei arbeiten zu können, müssen 2 Kanäle zur Floppy geöffnet werden. Zum ersten ist das der Kommandokanal (Kanal 15) und zweitens ein beliebiger anderer Kanal. Der Kommandokanal ist notwendig um den Positionierbefehl zu übertragen. Mit dem anderen Kanal wird die Datei eröffnet und bearbeitet.

1.a) Öffnen des Kommandokanals
130 CLOSE 15:0PEN 15,8,15

Sicherheitshalber sollte man vor jedem OPEN ein CLOSE setzen, um eine eventuelle Fehlermeldung »FILE OPEN ERROR» zu verhindern.

1.b) Eröffnen der Datei und Festsetzen der Datensatzlänge

Wie schon erwähnt, ist ein Merkmal der relativen Datei, daß jeder Datensatz die gleiche Länge besitzen muß. Diese Angabe muß beim Einrichten der Datei angegeben werden. Das geschieht mit folgendem Befehl:
OPEN lfn,ga,kanal,”dateiname,L,” + CHR$(datensatzlänge) (im Listing Zeile 140 und 11120)
lfn = logische filenummer
ga = Geräteadresse (normalerweise 8)
kanal (2-14)
dateiname = Name der Datei, wird so im Directory abgelegt, im Beispiel »ADR.REL«.
L = Kennzeichen für eine relative Datei
datensatzlänge = Summe aller Feldlängen (1-254)

Der Buchstabe »L« sagt dem DOS, daß eine relative Datei eröffnet wird. Diesem Buchstaben muß die Angabe der Datensatzlänge folgen. Sie wird mit dem CHR$ gesandt. Im Beispiel setzt sich ein Datensatz folgendermaßen zusammen:

NAME = 15 Buchstaben
VORNAME = 15 Buchstaben
STRASSE = 20 Buchstaben
POSTLZ = 4 Buchstaben
ORT = 15 Buchstaben
TELEFON = 12 Buchstaben

Das ergibt insgesamt 81 Buchstaben pro Datensatz, Da jeder Datensatz (auch RECORD genannt) mit einem RETURN abgeschlossen wird, erhöht sich die Datensatzlänge auf insgesamt 82 Zeichen.

1.c) Positionieren

Der Computer findet einen bestimmten Datensatz über die Datensatznummer, indem er sich den Dateianfang merkt und die entsprechende Anzahl Datensätze überspringt. Genauer gesagt, er positioniert auf diesen Datensatz. Und das wird mit dem Positionierbefehl erledigt. Dieses Kommando wird über den Kommandokanal (15) der Diskette gesandt. Seine Form ist:
PRINT #lfn,"P"+CHR$(kanal+ CHR$(low)+CHR$(high)+CHR$(byte) (siehe dazu Zeile 11130 - 11190)

Die Parameter »low« und »high« geben die Datensatz(=Record)nummer an. Da ein Byte maximal den Wert 255 annehmen kann, die Recordnummer aber höher sein darf, muß sie aufgeteilt werden in ein Low-Byte und ein High-Byte. Das geschieht einfach mit den Anweisungen
HB=INT(RN/256)
LB=RN-HB*256

RN = Recordnummer
HB = Höherwertiges Byte
LB = Niederwertiges Byte

Der letzte Parameter (Byte) positioniert auf eine bestimmte Stelle innerhalb eines Records. Beispiel:
PRINT#15,"P" + CHR$(2) + CHR$(12) + CHR$(l) + CHR$(8)

Hier wird auf das 8. Byte des 268. Records positioniert. Die 268 ist die Recordnummer und wird aufgeteilt in ein Low-Byte(12) und ein High-Byte(1). (HighByte*256 + Low-Byte = Recordnummer). Beim Schreiben eines Records muß jedoch immer auf das erste Byte positioniert werden.

Zum Schluß wird die Datei freigegeben.

1.d) Freigeben der Datei
PRINT#15,CHR$(255)

Diese Anweisung bewirkt, daß alle Records, die unter der angegebenen Recordnummer liegen, mit CHR$(255) beschrieben werden, sofern sie noch nicht anders belegt wurden. Das dauert seine Zeit. Je mehr Datensätze eingerichtet (freigegeben) werden, desto mehr Zeit beansprucht diese Arbeit. Aber erst diese Prozedur erlaubt ein fehlerfreies Lesen und schnelles Schreiben von Datensätzen. Wollen Sie einen Record beschreiben, der oberhalb des zuletzt freigegebenen Records liegt, so werden automatisch alle Records, die zwischen dem letzten und dem gerade beschriebenen Record liegen, freigegeben, das heißt, mit CHR$(255) beschrieben. Um diese Prozedur zu vermeiden, sollten Sie nur das erste Mal, beim Einrichten der Datei, die maximal zu erwartende Anzahl Records freigeben. Die Fehlermeldung »RECORD NOT PRESENT«, die dann im Floppy-Fehlerkanal erscheint, kann ignoriert werden, da dieser Record nur beschrieben (freigegeben) wird.

2. Speichern (Schreiben) eines Datensatzes

Zum Speichern eines Datensatzes sind folgende Funktionen durchzuführen:

2.a) Eingabe der Recordnummer und Aufteilung in Low- und High-Byte (siehe oben).

2.b) Eingabe der Daten (zum Beispiel über Input). Hier wird auch sichergestellt, daß die einzelnen Felder nicht länger werden, als vorher geplant wurde.

2.c) maximale Datensatzlänge bilden.

Das wird im Beispiel (siehe Listing) in Zeile 7000 bis 7999 durchgeführt. Es wird ein String mit der maximalen Länge (im Beispiel 81) gebildet. Dazu wird vorher eine Variable (im Listing BL$) definiert, mit der Anzahl Leerstellen, die das längste Feld unseres Datensatzes besitzt (im Beispiel das Feld »STRASSE« mit 20 Zeichen, also enthält BL$ 20 Leerstellen). Jedes Feld unseres Datensatzes wird entsprechend seiner vorher festgelegten Länge und abhängig von der wirklichen Länge bei der Eingabe mit den notwendigen Leerstellen aufgefüllt. Im Beispiel hat die Variable RC$ dann die Länge von 81 Zeichen und sie enthält den vollständigen Datensatz.

2.d) Speichern

Das Speichern dieses Datensatzes ist dann schnell geschehen: Es wird auf die vorher (in 2.a) angegebene Recordnummer positioniert und danach mit PRINT#1,RC$ gespeichert.
Mit PRINT # kann man jedoch nur Datensätze abspeichern, die nicht länger als 88 Zeichen sind. Sonst muß man die Daten Zeichen für Zeichen mittels einer GET# -Schleife schreiben und lesen.

3. Lesen eines Datensatzes

Hier erfolgt die Reihenfolge fast umgekehrt wie beim Schreiben. Zuerst muß jedoch auch hier die Nummer des gesuchten Datensatzes eingegeben werden. Nach entsprechender Aufteilung in Low- und High-Byte wird auf den Record positioniert und anschließend mit INPUT#1, RC$ von der Disk gelesen.

3.a) Eingabe Recordnummer und Aufteilung in Low- und High-Byte.

3.b) Lesen des Datensatzes

3.c) Aufteilen des Datensatzes

Dieser Vorgang muß umgekehrt dem Verketten (2.c) geschehen. Es wird ja lediglich der komplette Datensatz in die Variable RC$ gelesen. In Zeile 5000 bis 5999 werden wieder die einzelnen Felder gebildet. Danach fehlt nur noch die

3.d) Anzeige des Datensatzes

Das wars auch schon. Wenn Sie sich einmal alles gut durch den Kopf gehen lassen und die Problematik am Beispiel durchgehen und ausprobieren, können Sie in Zukunft Ihre eigene relative Datei programmieren.

Es ist gar nicht so schwierig, wie es auf den ersten Blick aussehen mag. Sie können auch Teile dieses Programms in Ihre eigenen einbauen. Schließlich ist das der Vorteil eines strukturierten Programmablaufs.

Zum Schluß möchte ich Sie noch auf einige Besonderheiten im Listing hinweisen.

Zeile 11110: Löschen einer eventuell bestehenden relativen Datei.

Zeile 11200-11220: Lesen des Fehlerkanals. Wenn der Fehler 52 gemeldet wird, bedeutet dies, daß die Anzahl Datensätze, die Sie angegeben haben, die Speicherkapazität der Diskette sprengen würde. Der Fehler 50 (RECORD NOT PRESENT, siehe auch Zeile 9050) bedeutet, daß Sie einen Datensatz lesen wollen, der nicht freigegeben wurde. Beim Schreiben braucht diese Fehlermeldung nicht beachtet werden (siehe 1.d).

Zeile 3060: Wenn ein gelesener Datensatz den Wert CHR$(255) besitzt, wird davon ausgegangen, daß er noch nicht beschrieben wurde. Deshalb wäre es auch sinnlos, Ihn anzeigen zu wollen. Dieser Vergleich wird in Zeile 9120-9130 nocheinmal in anderer Form durchgeführt, kann dort jedoch weggelassen werden.

Zeile 1170: Wenn das Programm beendet werden soll, muß der zur Positionierung notwendige Kanal 15 wieder geschlossen werden.

Anzumerken ist noch, daß nicht mehr als eine relative Datei gleichzeitig geöffnet werden kann. Es ist lediglich möglich, noch eine zusätzliche sequentielle Datei zur gleichen Zeit zu nutzen. Und diese Möglichkeit erlaubt uns, auch komfortablere Suchkriterien als über die Recordnummer einzusetzen. Doch darüber mehr in der übernächsten Ausgabe des 64'er Magazins.

(gk)
100 rem  ******************************
110 rem  * adressendatei 64'er/7 rel  *
120 rem  ******************************
130 close 15:open 15,8,15
140 close 1:open 1,8,2,"adr.rel,l,"+chr$(82)
1000 rem ----------------------------
1010 rem -  auswahl                 -
1020 rem ----------------------------
1030 :
1040 print "{clr}    relative datei 64'er 7"
1050 print :print :print
1060 print "  1 = datei einrichten"
1070 print "  2 = daten eingeben"
1080 print "  3 = suchen und anzeigen"
1090 print
1100 print "  x = programmende"
1110 print :print
1120 print "waehle"
1130 get r$:if r$="" then 1130
1140 if r$="1" then gosub 11000
1150 if r$="2" then gosub 4000
1160 if r$="3" then gosub 3000
1170 if r$="x" then close 1:close 15:end
1180 goto 1000
2000 rem  ------------------------------
2010 rem  - bildschirmanzeige
2020 rem  ------------------------------
2030 :
2040 print "{clr}    anzeige datensatz"
2050 print
2060 print " nummer ";rn:print
2070 print
2080 print " name    : "nv$" "nn$
2090 print " strasse : "sr$
2100 print " plz/ort : "pl$" "ot$
2110 print " telefon : "te$
2120 print :print :print
2130 return
2140 :
3000 rem ----------------------------
3010 rem - suchen
3020 rem ----------------------------
3030 :
3040 gosub 13000:rem  datensatznr.eingabe
3050 gosub 9000:rem  lesen
3060 if rc$<>chr$(255) then 3120
3070 print "{clr}"
3080 print :print :print
3090 print " nocht nicht beschrieben"
3100 print :print
3110 goto 3140
3120 gosub 5000:rem  aufteilen
3130 gosub 2000:rem  anzeigen
3140 print " druecke taste"
3150 get r$:if r$="" then 3150
3160 r$=""
3170 return
3180 :
4000 rem -----------------------------
4010 rem -       eingabe             -
4020 rem -----------------------------
4030 :
4040 gosub 13000:rem  datensatznr.
4050 gosub 6000:rem  eingabe
4060 gosub 7000:rem  verketten
4070 gosub 8000:rem  speichern
4080 return
4090 :
5000 rem ------------------------------
5010 rem -aufteilen datensatz in felder
5020 rem ------------------------------
5030 :
5040 nv$=left$(rc$,15)
5050 nn$=mid$(rc$,16,15)
5060 sr$=mid$(rc$,31,20)
5070 pl$=mid$(rc$,51,4)
5080 ot$=mid$(rc$,55,15)
5090 te$=mid$(rc$,70,12)
5100 return
6000 rem ------------------------------
6010 rem - eingabe neue daten         -
6020 rem ------------------------------
6030 :
6040 print "{clr}"
6050 print "    eingabe neue daten"
6060 print :print
6070 print "    nummer ";rn
6080 print :print
6090 input "nachname ";nn$:nn$=left$(nn$,15)
6100 input "vorname  ";nv$:nv$=left$(nv$,15)
6110 input "strasse  ";sr$:sr$=left$(sr$,20)
6120 input "postlz.  ";pl$:pl$=left$(pl$,4)
6130 input "ort      ";ot$:ot$=left$(ot$,15)
6140 input "telefon  ";te$:te$=left$(te$,12)
6150 print :print
6160 print "  adresse ok (j/n) ?"
6170 get r$:if r$="" then 6170
6180 if r$="n" then 6040
6190 if r$<>"j" then 6170
6200 return
6210 :
7000 rem ----------------------------
7010 rem -   verketten der felder   -
7020 rem ----------------------------
7030 :
7040 bl$="                    "
7050 rc$=nv$+left$(bl$,15-len(nv$))
7060 rc$=rc$+nn$+left$(bl$,15-len(nn$))
7070 rc$=rc$+sr$+left$(bl$,20-len(sr$))
7080 rc$=rc$+pl$+left$(bl$,4-len(pl$))
7090 rc$=rc$+ot$+left$(bl$,15-len(ot$))
7100 rc$=rc$+te$+left$(bl$,12-len(te$))
7110 return
7120 :
8000 rem  ----------------------------
8010 rem  - speichern daten auf disk -
8020 rem  ----------------------------
8030 :
8040 print# 15,"p"+chr$(2)+chr$(lb)+chr$(hb)+chr$(1)
8050 print# 1,rc$
8060 return
8070 :
9000 rem  ----------------------------
9010 rem  - lesen datensatz von disk -
9020 rem  ----------------------------
9030 f=0
9040 print# 15,"p"+chr$(2)+chr$(lb)+chr$(hb)+chr$(1)
9050 input# 15,er:rem  fehlerkanal
9060 if er<>50 then 9110:rem  record not present-error
9070 print "{clr} datensatznummer zu hoch"
9080 print :print " druecken sie eine taste"
9090 get r$:if r$="" then 9090
9100 run
9110 input# 1,rc$
9120 if asc(rc$)<>255 then 9140
9130 rem  datensatz ist noch frei
9140 return
11000 rem  --------------------------
11010 rem   - neue datei anlegen
11020 rem  --------------------------
11030 print "{clr}  achtung, eine bestehende datei mit"
11040 print :print "   dem namen 'adr.rel' wird geloescht"
11050 print
11060 print " (w)eiter  (z)urueck"
11070 get r$:if r$="" then 11070
11080 if r$<>"w" then return
11090 print
11100 print :print "bitte warten"
11110 close 15:open 15,8,15,"s:adr.rel"
11120 close 1:open 1,8,2,"adr.rel,l,"+chr$(82)
11130 print "wieviele datensaetze soll die datei "
11140 print "verwalten? ";
11150 input rn
11160 hb=int(rn/256)
11170 lb=rn-hb*256
11180 print :print" bitte warten"
11190 print# 15,"p"+chr$(2)+chr$(lb)+chr$(hb)+chr$(1)
11200 input# 15,er:rem  fehlerkanal
11210 if er<>52 then 11250:rem  datei zu gross
11220 print "     datei  zu gross"
11230 print :print " druecken sie eine taste"
11240 get r$:if r$="" then 11270
11250 print# 1,chr$(255)
11260 close 1:close 15
11270 run
13000 rem ----------------------------
13010 rem - eingabe datensatznummer
13020 rem ----------------------------
13030 :
13040 print "{clr}"
13050 print :print :print
13060 print " eingabe der datensatznummer"
13070 input rn
13080 hb=int(rn/256)
13090 lb=rn-hb*256
13100 return
Programmierung einer relativen Datei
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →