Kommen wir zu der flexibleren Methode.
Hierbei halten wir wie oben an den
Feldlängen fest. Da wir nun ja wissen, wie lang ein Feldeintrag maximal sein
kann, können wir im Prinzip vorausberechnen, an welcher Stelle im Datensatz
welches Feld zu finden, bzw. abzulegen
ist, ohne daß sich zwei Felder überschneiden. Hierbei müssen wir darauf
achten, daß entweder ein Feld immer so
lang ist, wie es maximal sein darf ( bei
kürzeren Einträgen wird der Rest einfach
mit SPACE-Zeichen aufgefüllt), oder aber
wir verwenden den Trick mit der GET#- Abfrage beim Lesen. Letzeres ist wohl
die eleganteste Lösung, da sie weniger
Programmieraufwand erfordert und gleichzeitig die Leseund Schreibzugriffe
beschleunigt, da immer nur so viele Zeichen gelesen werden, wie auch wirklich
benötigt werden ( und nicht immer die
maximale Feldlänge) . Wollen wir nun einmal ausrechnen, an welchen Bytepositionen die einzelnen Felder abgelgt werden:
Feldename Beginn Länge
Name 1.Byte 20 Bytes Vorname 21.Byte 15 Bytes Straße 36.Byte 30 Bytes PLZ 66.Byte 4 Bytes Ort 70.Byte 30 Bytes Telefon 100.Byte 15 Bytes
Anhand der Länge eines Feldes können wir
immer die erste Position des folgendenden Feldes berechnen, indem wir die Anfangsposition im Datensatz mit der
Feldlänge addieren.
Nun wissen Sie ja, daß man beim Positionierbefehl nicht nur die Datensatznummer, sondern auch die Byteposition innerhalb des gewählten Datensatzes angeben kann. Und über diese Methode können wir nun ganz bequem jede der 6 Feldpositionen einstellen und den Feldeintrag hineinschreiben. Beim Lesen können
wir, da wir für jedes Feld ja die An- fangsposition innerhalb eines Datensatzes kennen, dieses ebenfalls direkt
anwählen und Lesen. Das ist vor allem
beim Sortieren einer REL-Datei von Vorteil, da wir nun nach allen sechs Feldern eine Datei beliebig sortieren können, ohne die übrigen fünf Felder noch
extra einlesen zu müssen. Das erhöht die
Arbeitsgeschwindigkeit ungemein. Ein
anderer Geschwindigkeitsvorteil ergibt
sich beim Ändern von Datensätzen. Wollen
wir zum Beispiel in einer Adressdatei
nur die Straße verändern, weil die betreffende Person umgezogen ist, so genügt es auf das Feld " Straße"( Byte 36 im entsprechenden Datensatz) zu positionieren und den neuen Eintrag hineinzuschreiben. Hierbei müssen Sie jedoch
auch beachten, daß der neue Straßenname
unter Umständen kürzer ist, als der alte. Wir müssen dann nämlich noch ein
CHR$(255) nachschicken, damit unsere
GET#- Schleife auch ihre Endmarkierung
findet. Andernfalls würde Sie die rest- lichen Zeichen des alten Straßennamens
mitlesen. Ein Beispiel:
Alte Straße --> "Bahnhofstrasse 76" Neue Straße --> "Am Brunnen 2" Ergebnis beim Lesen OHNE eine neue Endmar- kierung --> "Am Brunnen 2se 76"
Schreiben Sie aber aich bitte nur dann
ein CHR$(255) danach, wenn der neue
Straßenname kleiner als die maximale
Feldlänge ist. Andernfalls würden Sie
wieder über die Feldgrenze hinausschreiben und so das erste Zeichen des darauffolgenden Feldes überschreiben!
SORTIEREN ODER INDIZIEREN:
Oben habe ich schon einmal die Möglichkeit der Sortierung einer Datenbank angesprochen. Die augenscheinlich einfachste Art der Sortierung wäre wohl das alphabetische Ordnen der Einträge eines
Feldes ( z. B. des Namensfeldes), und das
anschließende Umkopieren der Datensätze, so daß im ersten Datensatz auch wirklich
der alphabetisch erste und im letzten
Datensatz der alphabetisch letzte Name
steht. Diese Lösung verwendet aber wohl
keiner, da man sich vorstellen kann, daß
die Sortierung durch das Umkopieren der
Datensätze einen extremen Zeitaufwand
bedeutet ( gerade bei einfachen Sortieralgorithmen ist die Anzahl der Austausche zwischen zwei Einträgen ungemein
hoch) . Zusätzlich erfordert diese Methode nach jedem Neueintrag das Umkopieren
der kompletten REL-Datei, da ein neuer
Datensatz ja ebenfalls irgendwo einsortiert werden muß, und damit alle folgenden Datensätze einen Datensatz weiter
nach hinten rücken. Lassen Sie uns also
diese Methode in dem Mülleinmer werfen
und die schnelle, komfortable und flexible Methode herauskramen:" Indizierung" heißt das Zauberwort!
Im Prinzip bedeutet dieses Wort nicht
mehr als " Sortieren", jedoch ist die
Handhabung etwas anders. Hier möchte ich
noch einmal auf das Beispiel der Adressverwaltung zurückgreifen. Gehen wir also
davon aus, daß wir unsere Adressen nach
den Namen der eingetragenen Personen
ordnen wollen. Zu diesem Zweck lassen
wir unseren Sortieralgorithmus nach und
nach alle Namen aus der REL-Datei auslesen und alphabetisch in eine Liste einordnen. Dabei wollen wir uns zu jedem
Namen auch seine Datensatznummer merken.
Letztere können wir dann als Referenz
auf den alphabetisch richtigen Datensatz
verwenden. Ich möchte Ihnen dies anhand
eines Beispiels verdeutlichen. Nehmen wir
einmal eine Adressdatei, die die folgenden vier Namenseinträge, in der Reihenfolge in der sie eingegeben wurden, enthält:
Satznummer Eintrag (Name)
1 Müller 2 Becker 3 Schmidt 4 Meier
Nun sortieren wir unsere Datei nach Namen und erhalten folgende Reihenfolge:
2 Becker 4 Meier 1 Müller 3 Schmidt
Die aphabetisch richtige Reihenfolge
legen wir nun z. B. in einem Variablenfeld ab. Dieses Variablenfeld nennt man
Index. Wenn wir nun den alphabetisch
ersten Eintrag lesen wollen, so schauen
wir uns einfach den ersten Eintrag in
der Indexliste an, und lesen den Daten- satz ein, der dort steht - im Beispiel
also den zweiten.
Damit wir nicht jedes mal neu sortieren
müssen ist es ratsam die Indexliste in
einem sequentiellen File auf Diskette
abzuspeichern. Wenn wir nun jedesmal, wenn die Adressverwaltung gestartet wird
das Indexfeld einlesen, so können wir
ganz einfach und schnell alphabetisch
geordnete Datensätze finden und bearbeiten. Ebenso sollten wir darauf achten, daß neue Datensätze in die Indexliste
richtig einsortiert werden und der Index
wieder neu auf Diskette gespeichert
wird.
Die Indizierung bietet jedoch noch weitere Vorteile. So können wir auch ganz
beliebig nach verschiedenen Feldern sortieren und gleichzeitig beide sortierten
Dateien weiterverwenden. Wenn Sie Ihre
Adressen manchmal besser über den Vornamen finden können, so ist es sinnvoll
auch nach dem Vornamen zu indizieren und
diesen Index als Alternative Verwenden zu können. Beim Umschalten zwischen zwei
Indizes müssen Sie dann jeweils die neue
Indexdatei einlesen. Dies ist sogar bequem möglich, da wir zu der einen offenen REL-Datei auch noch eine SEQ-Datei
öffnen dürfen, ohne daß der 64 er
verrückt spielt. Wir können so als deh eit den Index wechseln, ohne dabei
noch umständlicherweise die REL-Datei
schließen und anschließend wieder öffnen
zu müssen.
EIN WORT ZU NUMERISCHEN EINTRÄGEN:
Vielleicht wollen Sie irgenwann einmal
auch numerische Einträge in einer REL-Datei speichern. Das wäre bei unserer
Adressverwaltung zum Beispiel bei dem
Postleitzahlenfeld sehr gut denkbar. In
manchen Fällen kann uns das auch einen
Speicherplatzvorteil im Gegensatz zu der
Speicherung als String geben. Hierbei
müssen wir jedoch immer gewisse Regeln beachten, sonst werden Sie ganz schön
Probleme mit den Datensatzlängen bekommen. Schreiben Sie nämlich eine numerische Variable ( wie z. B." A" als Float-, oder " A%" als Integervariable) mittels
" PRINT#2, A"( oder " PRINT#2, A%") in eine
Datei, so wandelt die PRINT#- Routine
Ihre Variable immer in eine Zeichenkette, also eine String, um. Wenn diese
numerische Variable nun verschieden viele Stellen besitzen kann, so werden Sie
ganz schöne Schwierigkeiten bekommen.
Logischerweise hat die Zahl "100" eine
Stelle weniger als die Zahl "1000" . Hinzu kommt, daß auch noch ein "-" als Vorzeichen davor stehen kann und daß der
PRINT#- Befehl immer noch ein SPACE-Zeichen ("") vor einer Zahl ausgibt, oder daß ganz kleine oder ganz große
Zahlen in der wissenschaftlichen
Schreibweise ( also z. B."3 .456+ E10") ausgegeben werden.
Wie lang sollen wir dann unser Feld nun
machen? Eine Antwort darauf ist schwierig, da das dann immer auch ganz von dem
Verwendungszweck abhängt. Entweder müssen Sie dem Benutzer schon bei der Eingabe der Zahlen gewisse Einschränkungen
auferlegen, oder aber durch eigene Umkonvertierung einer Zahl eine bestimmte
Bytelänge festlegen können.
Letzteres möchte ich hier als Beispiel
aufzeigen. Wir wollen uns auf positive
Integerzahlen beschränken, also ganze
Zahlen ( ohne Nachkommastellen), die von
0 bis 65535 gehen. In diesem Fall genügt
es eine solche Zahl in Lowund Highbyte
aufzuspalten und die beiden CHR$- Codes
zu speichern. In der Praxis sieht das
ähnlich wie in der Syntax des Positionierbefehls für REL-Dateien aus:
ZA=1992 HI=INT(ZA/256): LO=ZA-256*LO PRINT#2,CHR$(LO);CHR$(HI);
Damit können S) e nun alle oben genannten
Zahlen schreiben. Aber eben nur diese.
Möchten Sie z. B. Float-Zahlen schreiben, so sollten Sie sich überlegen, wie groß
oder klein diese maximal sein können.
Dann können Sie sie sich einmal mit dem
normalen PRINT-Befehl auf dem Bildschirm
ausgeben lassen und die Stellen, die sie
einnehmen abzählen. Wenn Sie sicher sein
können, daß dies die maximale Länge einer Ihrer Zahlen ist, so können Sie sie
als Feldlänge definieren. Beachten Sie
aber auch immer, daß der PRINT#- Befehl
vor einer Zahl immer ein Leerzeichen
druckt und daß zusätzlich immer noch ein
Minus-Zeichen vor einer Zahl stehen
kann. Außerdem können Sie solche Zahlen
auch nur mittels des INPUT#- Befehls wieder einlesen, weshalb Sie noch ein Zeichen mehr für das CR mitrechnen sollten.
Ein anderer Weg um Float-Zahlen zu speichern wäre das direkte übernehmen der
5- Byte-Mantissen- Darstellung, wie das
Betriebssystem des 64 ers sie benutzt,
jedoch ist diese Möglichkeit relativ
kompliziert, da sie den intensiven Gebrauch von Betriebssystemsroutinen erfordert, wovon wir momentan die Finger
lassen wollen ( in einer der späteren
Folge: dieses Kurses werde ich aber noch
einmal auf dieses Thema zurückkommen) .
So. Das wäre es dann wieder einmal für
diesen Monat. In der nächsten Ausgabe
der " Magic Disk" wollen wir uns mit den
Direktzugriffsbefehlen der Floppy befassen, die es uns ermöglichen, auf die
Diskettenblocks direkt zuzugreifen.
(ub)