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 vorausbe- rechnen, an welcher Stelle im Datensatz welches Feld zu finden, bzw. abzulegen ist, ohne daß sich zwei Felder über- schneiden. 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 gleich- zeitig die Lese- und Schreibzugriffe beschleunigt, da immer nur so viele Zei- chen gelesen werden, wie auch wirklich benötigt werden (und nicht immer die maximale Feldlänge). Wollen wir nun ein- mal ausrechnen, an welchen Bytepositio- nen 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 folgenden- den Feldes berechnen, indem wir die An- fangsposition im Datensatz mit der Feldlänge addieren. Nun wissen Sie ja, daß man beim Positio- nierbefehl nicht nur die Datensatznum- mer, sondern auch die Byteposition in- nerhalb des gewählten Datensatzes an- geben kann. Und über diese Methode kön- nen wir nun ganz bequem jede der 6 Feld- positionen einstellen und den Feldein- trag hineinschreiben. Beim Lesen können wir, da wir für jedes Feld ja die An- fangsposition innerhalb eines Datensat- zes kennen, dieses ebenfalls direkt anwählen und Lesen. Das ist vor allem beim Sortieren einer REL-Datei von Vor- teil, da wir nun nach allen sechs Fel- dern eine Datei beliebig sortieren kön- nen, 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 be- treffende Person umgezogen ist, so ge- nügt es auf das Feld "Straße" (Byte 36 im entsprechenden Datensatz) zu positio- nieren und den neuen Eintrag hineinzu- schreiben. Hierbei müssen Sie jedoch auch beachten, daß der neue Straßenname unter Umständen kürzer ist, als der al- te. 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 hinausschrei- ben und so das erste Zeichen des darauf- folgenden Feldes überschreiben!
SORTIEREN ODER INDIZIEREN:
Oben habe ich schon einmal die Möglich- keit der Sortierung einer Datenbank an- gesprochen. Die augenscheinlich einfach- ste 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 Sortier- algorithmen ist die Anzahl der Austau- sche zwischen zwei Einträgen ungemein hoch). Zusätzlich erfordert diese Metho- de nach jedem Neueintrag das Umkopieren der kompletten REL-Datei, da ein neuer Datensatz ja ebenfalls irgendwo einsor- tiert werden muß, und damit alle folgen- den 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 flexi- ble 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 Adress- verwaltung 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 ausle- sen und alphabetisch in eine Liste ein- ordnen. 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 folgen- den vier Namenseinträge, in der Reihen- folge 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 Na- men und erhalten folgende Reihenfolge:
2 Becker 4 Meier 1 Müller 3 Schmidt
Die aphabetisch richtige Reihenfolge legen wir nun z.B. in einem Variablen- feld 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 bearbei- ten. 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 wei- tere Vorteile. So können wir auch ganz beliebig nach verschiedenen Feldern sor- tieren und gleichzeitig beide sortierten Dateien weiterverwenden. Wenn Sie Ihre Adressen manchmal besser über den Vorna- men 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 be- quem möglich, da wir zu der einen offe- nen REL-Datei auch noch eine SEQ-Datei öffnen dürfen, ohne daß der 64er 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 bekom- men. Schreiben Sie nämlich eine numeri- sche 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 Zeichenket- te, also eine String, um. Wenn diese numerische Variable nun verschieden vie- le Stellen besitzen kann, so werden Sie ganz schöne Schwierigkeiten bekommen. Logischerweise hat die Zahl "100" eine Stelle weniger als die Zahl "1000". Hin- zu kommt, daß auch noch ein "-" als Vor- zeichen 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 schwie- rig, da das dann immer auch ganz von dem Verwendungszweck abhängt. Entweder müs- sen Sie dem Benutzer schon bei der Ein- gabe der Zahlen gewisse Einschränkungen auferlegen, oder aber durch eigene Um- konvertierung 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 Low- und Highbyte aufzuspalten und die beiden CHR$-Codes zu speichern. In der Praxis sieht das ähnlich wie in der Syntax des Positio- nierbefehls 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 ei- ner 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 wie- der einlesen, weshalb Sie noch ein Zei- chen mehr für das CR mitrechnen sollten. Ein anderer Weg um Float-Zahlen zu spei- chern wäre das direkte übernehmen der 5-Byte-Mantissen-Darstellung, wie das Betriebssystem des 64ers sie benutzt, jedoch ist diese Möglichkeit relativ kompliziert, da sie den intensiven Ge- brauch von Betriebssystemsroutinen er- fordert, 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 befas- sen, die es uns ermöglichen, auf die Diskettenblocks direkt zuzugreifen.
(ub)