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)