Artikel
dBase-Dateien –
Byte für Byte
Bekanntlich erzeugt dBase standardmäßig keine ASCII-Dateien, die von anderen Programmen einfach importiert oder weiterverarbeitet werden könnten, sondern verwendet ein eigenes Dateiformat. Zwar können viele Standardprogramme Dateien des dBase-Formats lesen, doch vor allem Programmierern anderer Sprachen kann eine genaue Kenntnis der Struktur von dBase-Dateien nützlich sein – und sei es nur, um die formatierten Daten weiterzuverarbeiten. Ein entsprechendes Listing in Quick Basic zeigt, wie es gemacht wird.
Dank seiner überragenden Marktstellung ist das dBase-Format neben dem ASCII-Format das am weitesten verbreitete. Natürlich ist es für dBase-Programmierer nicht notwendig, sich mit den einzelnen Bytes einer dBase-Datei herumzuschlagen, da dies dBase selbständig erledigt. Doch versuchen Sie einmal, aus einer defekten Bestandsdatei zu retten, was noch zu retten ist. Oder Sie beabsichtigen, auf die unter dBase gespeicherten Informationen mit Hilfe eines (schnelleren) Programms zuzugreifen, das Sie beispielsweise in Assembler, in C oder in (Quick) Basic schreiben möchten, weil Sie komplizierte grafische Auswertungen oder mathematische Berechnungen durchführen oder die Daten in ein Fremdformat konvertieren wollen, das von dBase nicht unterstützt wird. Dazu benötigen Sie genaue Angaben darüber, auf welche Weise dBase-Daten gespeichert werden.
Dieser Artikel informiert im ersten Teil über die Anatomie von dBase-III-Plus-/dBase-IV-Dateien. Im (zweiten Teil (Listing) wird anhand eines in Quick Basic geschriebenen Beispielprogramms gezeigt, wie Sie mit Hilfe fremder Programmiersprachen auf die Daten einer dBase-Datei zugreifen.
Kenntnisse über die Anatomie von dBase-Bestandsdateien sind für den Fremdprogrammierer unerläßlich
Damit die dBase-Datei – wie üblich – mit einigen Daten versehen ist, soll von folgender Beispieldatei ausgegangen werden, die gemäß der sequentiellen Speicherung mit zwei Datensätzen ausgestattet ist:
Titel des Films: »Ninotschka«
Regisseur: »Lubitsch«
Wie oft gesehen?: »8«
Wann zuletzt gesehen?: »11.12.89«
Noch einmal sehen (J/N) ? : »T«
Bemerkung: »Greta Garbo als Ninotschka!«
Titel des Films: »Casablanca«
Regisseur: »Curtiz«
Wie oft gesehen?: »12«
Wann zuletzt gesehen? : »12.12.89«
Noch einmal sehen (J/N) ? : »F«
Bemerkung:
Dabei steht »T« beziehungsweise »F« bei »noch einmal sehen« für »wahr« (»(T)rue«) oder »ja« respektive »falsch« (»(F)alse«) oder »nein«.
Zum Dateiaufbau noch einige terminologische Bemerkungen: Zusammenhängende Informationsblöcke, wie sie jeweils durch einen der vorhergehenden Absätze dargestellt werden, heißen Datensätze. Ein Datensatz in diesem Beispiel wäre also: »Ninotschka, Lubitsch, 8, 11.12.1989, T, Greta Garbo als Ninotschka!«. Die gleichartigen Informationseinheiten innerhalb der Datensätze werden in Datensatzfeldern gespeichert: Das Datensatzfeld »Regisseur« enthält also in dem einen Datensatz den Eintrag »Lubitsch«, im anderen den Eintrag »Curtiz«. Mehrere Datensätze bilden schließlich eine Dateiverwaltung.
E:\>debug film.dbf -L 0 -D 0 13F 2721:0000 8B 5A 07 09 02 00 00 00-E1 00 2F 00 00 00 00 00 .Z......../..... 2721:0010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 2721:0020 54 49 54 45 4C 00 00 00-00 00 00 43 00 00 00 00 TITEL......C.... 2721:0030 0F 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 2721:0040 52 45 47 49 53 53 45 55-52 00 00 43 00 00 00 00 REGISSEUR..C.... 2721:0050 0A 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 2721:0060 57 49 45 4F 46 54 47 45-53 00 00 4E 00 00 00 00 WIEOFTGES..N.... 2721:0070 02 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 2721:0080 57 41 4E 4E 5A 55 4C 47-45 53 00 44 00 00 00 00 WANNZULGES.D.... 2721:0090 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 2721:00A0 4E 4F 43 48 45 49 4E 4D-41 4C 00 4C 00 00 00 00 NOCHEINMAL.L.... 2721:00B0 01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 2721:00C0 42 45 4D 45 52 4B 55 4E-47 00 00 4D 00 00 00 00 BEMERKUNG..M.... 2721:00D0 0A 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 2721:00E0 0D 20 4E 69 6E 6F 74 73-63 68 6B 61 20 20 20 20 . Ninotschka 2721:00F0 20 4C 75 72 69 74 73 63-68 20 20 20 38 31 39 38 Luritsch 8198 2721:0100 39 31 32 31 31 54 30 30-30 30 30 30 30 30 30 31 91211T0000000001 2721:0110 20 43 61 73 61 62 6C 61-6E 63 61 20 20 20 20 20 Casablanca 2721:0120 43 75 72 74 69 7A 20 20-20 20 31 32 31 39 38 39 Curtiz 121989 2721:0130 31 32 31 32 46 20 20 20-20 20 20 20 20 20 20 1A 1212F . -
In dBase legen Sie nun eine DBF-Datei an, die sich für die Aufnahme der angegebenen Datensätze eignet, indem Sie aus dem Regiezentrum (dBase IV) beziehungsweise Assistenten (dBase III Plus) oder mit Hilfe des Create-Befehls zunächst die Struktur der Bestandsdatei bestimmen. Sie legen dabei fest, welche Felder die Bestandsdatei enthalten soll, welche Art von Informationen in den Feldern gespeichert wird und wie lang die Felder werden, die die Daten aufnehmen. Die Beispieldatei soll die Struktur aus Tabelle 1 haben.
Nachdem die Struktur der Datei festgelegt worden ist, kann im Assistenten, im Regiezentrum oder mit Hilfe des Append-Befehls mit der Eingabe der Informationen begonnen werden. Angenommen also, Sie hätten unter dBase eine Bestandsdatei (sie heiße »film.dbf«) mit der angegebenen Struktur angelegt und die genannten Daten eingegeben. Wie werden die Informationen von dBase intern gespeichert? Eine Möglichkeit, sich eine Datei byteweise anzeigen zu lassen, bietet das MS-DOS-Dienstprogramm »Debug«. Hexadezimaleditoren, wie sie zum Beispiel mit den Norton Utilities oder PC-Tools geliefert werden, eignen sich besser, wenn Sie es mit großen Dateien zu tun haben – und sie bieten außerdem komfortablere Bearbeitungsmöglichkeiten als das erwähnte »Debug«. Bild 1 zeigt Aufruf und Anzeige von Debug. Der Aufruf gestaltet sich noch einfach:
debug film.dbf
Hinter der Eingabeaufforderung des Debuggers (dargestellt durch einen Bindestrich) sorgt die Anweisung
L 0
– also »Load« – dafür, daß die Datei »film.dbf« an die Adresse 0000hex (relativ zu einer durch das CS-Register festgelegten Segmentadresse) im Arbeitsspeicher geladen wird. Die zweite Anweisung
D 0 13F
– also »Dump« – veranlaßt das Debug-Programm, den Inhalt des Arbeitsspeichers ab der Adresse 0000hex bis zur Adresse 013Fhex (319) Byte für Byte auszugeben. Demnach gibt Ihnen Debug von der Adresse 0 bis zur Adresse 13Fhex 320 Byte aus – genau so viel wie nötig, um die vollständige Datei »film.dbf« anzuzeigen, die eine Größe von 320 Byte hat, wie Sie mit dem Dir-Befehl überprüfen können.
Die linke Spalte des Ausgabebildschirms mit den durch Doppelpunkt getrennten Zahlenblöcken gibt Arbeitsspeicheradressen an – diese Spalte braucht Sie nicht zu interessieren. Der mittlere Block gibt die in den Arbeitsspeicher geladene Datei zeilenweise Byte für Byte aus, wobei jedes Ziffernpaar ein Byte im Hexadezimalformat angibt.
Wenn ein Byte für ein druckbares ASCII-Zeichen steht, wird dieses Zeichen im rechten Block der Debug-Ausgabe dargestellt. Nicht druckbare Zeichen werden durch einen Punkt repräsentiert.
Feld | Feldname | Typ | Länge | Dezimalstellen |
---|---|---|---|---|
1 | Titel | Zeichen | 15 | 0 |
2 | Regisseur | Zeichen | 10 | 0 |
3 | Wieoftges | numerisch | 2 | 0 |
4 | Wannzulges | Datum | 8 | 0 |
5 | Nocheinmal | logisch | 1 | 0 |
6 | Bemerkung | Memo | 10 | 0 |
┌────────────────────────────────────────────────────────────────┐ │Byte-Position Inhalt oder Bedeutung │ └────────────────────────────────────────────────────────────────┘ ┌────────────────────────────────────────────────────────────────┐ │ A. Dateivorspann │ ├────────────────────────────────────────────────────────────────┤ │ Allgemeine Datei-Informationen │ │ │ │ 1. Byte dBase-Version/Memodatei vorhanden? │ │ 2.- 4. Byte Datum der letzten Änderung (Jahr, Monat, Tag) │ │ 5.- 8. Byte Anzahl der Datensätze │ │ 9.-10. Byte Länge des. Dateivorspanns │ │11.-12. Byte Länge eines Datensatzes (inkl. Löschkennzeichen)│ │13.-14. Byte reserviert │ │15. Byte Transaktion abgeschlossen? (nur dBase IV) │ │16. Byte Datei verschlüsselt? (nur dBase IV) │ │17.-28. Byte reserviert │ │29. Byte Mdx- Indexdatei vorhanden? (nur dBase IV) │ │20.-32. Byte reserviert │ │----------------------------------------------------------------│ │ Feldinformationen (je Feld ein Block a 32 Byte) │ │ │ │ 1.-10. Byte Feldname (aufgefüllt durch 00hex) │ │11. Byte 00hex │ │12. Byte Feldtyp (»C«, »N«, »F«, »D«, »L«, oder »M«) │ │13.-16. Byte reserviert │ │17. Byte Feldlänge │ │18. Byte Anzahl der Dezimalstellen │ │19.-32. Byte reserviert │ │----------------------------------------------------------------│ │ Vorspannende │ │ │ │ 1 Byte Wert: 0Dhex │ ├────────────────────────────────────────────────────────────────┤ │ B. Datensätze │ ├────────────────────────────────────────────────────────────────┤ │ 1. Byte 2Ahex, falls Datensatz zum Löschen markiert, │ │ sonst: 20hex │ │ │ │ Es schließen sich die Feldinhalte ohne Begrenzungszeichen an.│ │ Die Feldinhalte sind als ASCII-Zeichen gespeichert. │ │ Die Zeichenfeldinhalte: linksbündig; │ │ Die Inhalte numerischer Felder: rechtsbündig. │ │ Die Felder haben jeweils die im Vorspann angegebene Länge. │ │ Nicht durch Daten benötigter Platz wird durch Leerzeichen │ │ (ASCII 20hex) gefüllt. (Einzelheiten im Text). │ ├────────────────────────────────────────────────────────────────┤ │ C. Dateiendemarke │ ├────────────────────────────────────────────────────────────────┤ │ 1 Byte 1Ahex │ └────────────────────────────────────────────────────────────────┘
Eine dBase-Datei besteht nun aus einem Vorspann, den eigentlichen Datensätzen und einem Byte, das das Ende der Datei markiert. Die Byteverteilung im einzelnen:
- Der Dateivorspann (auch »Dateiheader« oder -kopf genannt) besteht aus einem 32 Byte langen Block, der allgemeine Dateiinformationen enthält (das sind die ersten zwei Zeilen in Bild 1), aus einem Block, der je Datensatzfeld 32 Byte lang ist und die Feldbeschreibung beinhaltet (je Feld also ein Zeilenpaar, in Bild 1 von Zeile 3 bis 14), und schließlich aus einem einzelnen Byte, das das Vorspannende markiert.
- Allgemeine Dateiinformationen:
- Das erste Byte der Datei ist das am
schwierigsten zu interpretierende. Es hat mit der
dBase-Version zu
tun, unter der die Datei erzeugt wurde, und es enthält
außerdem die Information, ob die Datei Memofelder enthält
und somit eine Memodatei existieren sollte, in der Memoeinträge
(dies sind die bereits erwähnten Texteinträge variabler
Länge) gespeichert werden. Das erste Byte von
dBase-III-Plus-Dateien hat entweder
den Wert 03hex oder den Wert 83hex – im Dualsystem
ausgedrückt: 0000 0011 beziehungsweise 1000 0011.
Dabei stehen die Bits an den Stellen 0 und 1 (Bits werden von rechts nach links beginnend mit der Position 0 gezählt) für die dBase-Version III Plus. Wenn das siebte Bit auf 1 steht, existiert in der Bestandsdatei ein Memofeld – dBase sucht also beim Öffnen der Datei nach einer zugehörigen Memodatei. Steht das siebte Bit auf 0, existiert keine Memodatei. Alle Dateien, deren erstes Byte nicht einen der beiden genannten Werte (03hex oder 83hex) aufweist, werden von dBase III Plus nicht als dBase-Dateien akzeptiert. Das erste Byte einer dBase-IV-Datei hat ebenfalls den Wert 03hex (obwohl 04hex für die Version IV zu erwarten wäre), wenn die Datei kein Memofeld aufweist und ihr damit keine Memodatei zugeordnet ist. Wie dBase III Plus setzt auch dBase IV das siebte Bit des ersten Bytes einer Dbf-Datei auf 1, wenn ihr eine Memodatei zugeordnet ist. Allerdings setzt dBase IV auch Bit 3 auf 1, wenn die Bestandsdatei ein Memofeld enthält, so daß sich für Byte 1 bei vorhandener Memodatei dual 1000 1011 oder hexadezimal 8Bhex ergibt: Und das entspricht genau dem Beispiel, wo tatsächlich ein Memofeld vorgesehen ist – die Beispieldatei gemäß Bild 1 ist also in dBase IV erzeugt worden. Daß dBase IV das erste Byte einer Bestandsdatei auf 8Bhex und nicht wie dBase III Plus auf 83hex setzt, wenn die Datei ein Memofeld enthält, ist der Grund dafür, daß dBase-IV-Dateien mit Memofeldern von dBase III Plus nicht gelesen werden können.
Die eben gemachten Angaben über das erste Byte von dBase-IV-Dateien müssen mit der Einschränkung versehen werden, daß das erste Byte unter bestimmten Bedingungen – zum Beispiel im Zusammenhang mit SQL-Anwendungen – andere als die angeführten Standardwerte aufweisen kann. Allgemein gilt, daß dBase IV nur Dateien als dBase-Dateien akzeptiert, bei denen die linken drei Bits des Bytes auf 011 stehen. Steht außerdem Bit 7 auf 1, wird eine Memodatei erwartet. - Datum der letzten Änderung: Die nächsten drei Bytes einer dBase-Datei stehen für das Datum der letzten Änderung in der Reihenfolge Jahr, Monat, Tag – im vorliegenden Beispiel: 5A 07 09hex. Wenn Sie die Werte in Dezimalwerte umformen, ergibt sich als Datum der 09.07.90.
- Anzahl der Datensätze in der Datei: Die nächsten vier Bytes geben an, wie viele Datensätze enthalten sind. Dabei ist das am weitesten links stehende Byte das niedrigstwertige, das am weitesten rechts stehende das höchstwertige (ein Format, das für die IBM-Welt typisch ist). Im Beispiel steht das niedrigstwertige Byte auf 02hex, alle höherwertigen Bytes stehen auf 0. Demnach enthält die Bestandsdatei zwei Datensätze, was ja auch den Tatsachen entspricht.
- Länge des Vorspanns in Byte: Das neunte und zehnte Byte der Datei geben die Länge des Dateikopfs in Byte an – wiederum ist das neunte Byte das niederwertige, das zehnte das höherwertige. In unserem Beispiel ergibt sich eine Vorspannlänge von 125 (E1hex) Byte.
- Länge eines Datensatzes in Byte: Es folgt ein 2-Byte-Wert, der die Länge eines Datensatzes angibt. Ein Datensatz der Datei »film.dbf« enthält also 47 Byte (2Fhex).
- Es folgen zwei reservierte Bytes, das 15. Byte hat bei dBase-IV-Dateien den Wert 01hex, wenn ein Begin-Transaction-Befehl nicht abgeschlossen worden ist, und das 16. Byte steht auf 01hex (dBase IV), wenn die Datei verschlüsselt ist. Es folgt eine Reihe reservierter Bytes. Das 29. Byte hat bei dBase-IV-Dateien den Wert 01hex, wenn der Datei eine Mdx-Indexdatei zugeordnet ist. Die restlichen drei Bytes des ersten 32-Byte-Blocks sind wiederum reserviert – in der Beispieldatei stehen all diese Werte auf 0.
- Das erste Byte der Datei ist das am
schwierigsten zu interpretierende. Es hat mit der
dBase-Version zu
tun, unter der die Datei erzeugt wurde, und es enthält
außerdem die Information, ob die Datei Memofelder enthält
und somit eine Memodatei existieren sollte, in der Memoeinträge
(dies sind die bereits erwähnten Texteinträge variabler
Länge) gespeichert werden. Das erste Byte von
dBase-III-Plus-Dateien hat entweder
den Wert 03hex oder den Wert 83hex – im Dualsystem
ausgedrückt: 0000 0011 beziehungsweise 1000 0011.
- Feldinformationen: Im Vorspann folgt nun pro
Feld der Bestandsdatei ein 32-Byte-Block, der jeweils folgendermaßen
aufgebaut ist:
- Feldname: Die ersten zehn Bytes enthalten den Feldnamen. Sie werden durch ein folgendes 0hex-Byte abgeschlossen. Feldnamen können Buchstaben, Ziffern und Interpunktionszeichen enthalten, die im ASCII-Format gespeichert werden. Nicht durch Namenszeichen belegte Bytes haben den Wert 0hex – Feldnamen werden also nicht durch Leerzeichen (20hex) aufgefüllt. Vergleichen Sie dazu Bild 1, wo die ersten fünf Bytes, die den Feldnamen angeben, die Werte 54 49 54 45 4Chex haben. Einer ASCII-Tabelle können Sie entnehmen, daß diese Werte die Buchstaben »TITEL« codieren. Es folgen insgesamt sechs Bytes mit dem Wert 0hex.
- Feldtyp: Das 12. Byte jedes 32-Byte-Feldbeschreibungsblocks enthält ein Zeichen im ASCII-Format, das den Feldtyp angibt: »C« steht für Zeichenfelder (Character), »N« für numerische Felder (Numeric), »D« für Datumsfelder (Date), »L« für logische Felder (Logical), »M« für Memofelder und »F« (nur dBase IV) für Gleitkommafelder (Float). Die folgenden vier Bytes sind reserviert.
- Feldlänge in Byte: Das 17. Byte gibt die Feldlänge an (im Beispiel haben die Felder in dieser Reihenfolge die Längen 15, 10, 2, 8, 1 und 10 – jeweils dezimal angegeben). Zeichenfelder können höchstens eine Länge von 254 Byte, numerische und Gleitkommafelder höchstens eine Länge von 20 Byte haben. Datumsfelder sind immer 8 Byte lang, logische Felder 1 Byte und Memofelder (siehe unten) sind 10 Byte lang.
- Dezimalstellen: Das 18. Byte gibt die Anzahl der Dezimalstellen an, was natürlich nur bei numerischen und Gleitkommafeldern interessant ist. Die restlichen Bytes jedes 32-Byte-Feldbeschreibungsblocks sind reserviert.
- Ende des Vorspanns: Es folgt ein Byte, das immer den Wert 0Dhex hat und das Ende des Vorspanns markiert. Ein Wort zu den reservierten Bytes im Dateivorspann: Wenn Sie eine Datei unter dBase bearbeitet haben, kann es sein, daß einige der reservierten Bytes Werte annehmen, die von 0 verschieden sind – abhängig zum Beispiel von der Adresse im Arbeitsspeicher, an der Feldinhalte geladen waren, oder vom Arbeitsbereich, in dem die Datei geöffnet wurde. Wie auch immer, wenn alle reservierten Bytes auf 0 stehen, kann eine Datei problemlos von dBase bearbeitet werden.
- Datensätze: Die eigentlichen Daten
schließen sich an den Vorspann an. Jedem Datensatz geht
entweder ein Leerzeichen
(ASCII:
20hex) oder ein Stern (ASCII: 2Ahex) voran. Ein Stern besagt,
daß der folgende Datensatz zum Löschen markiert ist.
Jedes Feld eines Datensatzes wird in der im Vorspann festgelegten
Länge gespeichert, wobei die Feldinhalte von Zeichenfeldern
linksbündig, diejenigen von numerischen Feldern rechtsbündig
angeordnet werden. Der durch den Feldinhalt nicht benötigte Platz
wird durch Leerzeichen
(ASCII:
20hex), nicht durch Bytes mit dem Wert 0hex, gefüllt.
Alle Feldinhalte werden durch ASCII-Zeichen dargestellt; für
Zeichenfelder sind alle ASCII-Zeichen, für numerische Felder
Ziffern, der Dezimalpunkt und das Minuszeichen, für logische
Felder die Zeichen »J« (Ja), »N« (Nein),
»T« (True), »F«
(False) und das Fragezeichen (unbestimmter
Wahrheitswert) zugelassen. Datumsfelder werden im Format Jahr (vier
Ziffern), Monat (zwei Ziffern) und Tag (zwei Ziffern) gespeichert.
Es wird Ihnen leichtfallen, diese Angaben mit dem Beispiel in
Bild 1
zu vergleichen.
Einen Sonderstatus haben die Memofelder in einer Datei: Es wurde bereits erwähnt, daß die Memofeldeinträge Texteinträge von variabler Länge sein können, die nicht in der eigentlichen Bestandsdatei (Dbf-Datei) gespeichert werden, sondern in einer ihr zugeordneten Memodatei (mit dem Namen der Datei und der Ergänzung »dbt«). Im 10 Byte langen Memofeld innerhalb der Dbf-Datei wird lediglich eine Nummer gespeichert, die sich auf die Nummer des – normalerweise 512 Byte großen – Blocks innerhalb der Dbt-Datei bezieht, in dem der eigentliche Memotext zu dem fraglichen Datensatz gespeichert ist (oder in dem er beginnt, falls seine Länge 512 Byte überschreitet). In der Beispieldatei wurde eine Bemerkung über die Hauptdarstellerin des Lubitschfilms eingegeben. Demzufolge weist das Memofeld auf den Block 1 der zugeordneten Dbt-Datei. Der erste 512-Byte-Block der Dbt-Datei (Block 0) ist immer der Vorspann der Dbt-Datei und enthält Informationen über diese Datei. Da zum zweiten Datensatz der Beispieldatei keine Bemerkung eingegeben ist, verweist das Memofeld dieses Datensatzes nicht auf die Dbt-Datei, sondern enthält lediglich Leerzeichen.
Die einzelnen Felder der Datensätze und die einzelnen Datensätze werden im dBase-Format durch keinerlei Trennzeichen voneinander abgegrenzt. - Dateiendemarke: Das letzte Byte einer
dBase-Datei hat
immer den Wert 1Ahex und markiert das Ende der Datei.
Eine Zusammenfassung aller dieser Angaben gibt Ihnen Tabelle 2. - dBase im Griff
von Quick Basic (Version 4.5):
Das Listing gibt ein Quick-Basic-Utility
namens »Dbquick« wieder, das Ihnen die allgemeinen Dateiinformationen
(Bild 2),
die Feldbeschreibungen
(Bild 3)
und die Inhalte beliebiger Datensätze einer beliebigen
dBase-III-Plus- oder
dBase-IV-Datei in gut lesbarer
Form auf dem Bildschirm anzeigt. Wenn Sie dieses
Utility benutzen,
können Sie folglich die leidigen Hexadezimaldarstellungen
des letzten Abschnitts wieder vergessen. Das Programm, das Sie
natürlich erst compilieren (oder auf der Databox beziehen),
wird mit
dbquick
Ein Beispielprogramm zeigt die Dateiinformationen an
Die in diesem Zusammenhang interessanten Deklarationen und Anweisungen machen nur den kleinsten Teil des Programmcodes aus – sie werden im folgenden kurz beschrieben. Der größere Teil des Programms sorgt lediglich für die Benutzerführung und die Bildschirmgestaltung – diese Teile des Programms, die für das behandelte Thema weniger wichtig sind, werden nicht kommentiert. Zunächst werden zwei Datentypen, »Header« und »SatzStrukt«, deklariert (Zeilen 11 bis 35). Nach den Prozedurdeklarationen folgt die Deklaration einer Variablen vom Typ »Header« und eines (dynamischen) Arrays vom Typ »SatzStrukt«. Die Variable nimmt in der Prozedur »HoleDatei« die allgemeinen Dateiinformationen des Dateivorspanns auf. Im Array werden für jedes Feld die Feldinformationen des Dateivorspanns gespeichert. 1-Byte-Werte des Vorspanns werden dabei als Zeichenkette der Länge 1, 2-Byte-Werte als Integer- und 4-Byte-Werte als Long-Integer-Werte eingelesen. Ist ein 1-Byte-Wert numerisch, muß für die Ausgabe des entsprechenden Werts auf dem Bildschirm mit Hilfe der Asc-Funktion der (numerische) ASCII-Wert der Zeichenvariable ermittelt werden. Reservierte Bytes werden in Dummy-Variablen eingelesen.
Informationen über dBase-Datenbankdateien unter QUICK BASIC DKUNDEN.DBF Allgemeine Dateiinformationen ──────────────────────────────────────────────────────────────────────────────── Version: dBase III Plus / dBase IV Memodatei: nicht vorhanden Datum der letzten Änderung: 13.9.89 Anzahl der Datensätze: 33 Anzahl der Felder: 11 Länge der Datensätze in Byte: 195 (incl. Löschkennzeichen) Länge des Vorspanns in Byte: 385 Transaktionsbit: nicht gesetzt Datenbank: nicht kodiert MDX-Indexdatei: nicht vorhanden Dateilänge in Byte: 6821 ──────────────────────────────────────────────────────────────────────────────── I - Allgemeine DateiInfos * S - Struktur der Datenbank * D - Datensatznummer N - Nächster Datensatz * V - Vorheriger Datensatz * A - Andere Datei * E - Ende
Informationen über dBase-Datenbankdateien unter QUICK BASIC
DKUNDEN.DBF
Informationen über die Datensatzstruktur
────────────────────────────────────────────────────────────────────────────────
Feldname Feldtyp Feldlänge Dezimalstellen
--------------------------------------------------------------------------------
KUNDCODE Zeichen 8 0
ANREDE Zeichen 6 0
VORNAME Zeichen 18 0
NACHNAME Zeichen 18 0
FIRMA Zeichen 30 0
ANSCHRIFT Zeichen 30 0
ORT Zeichen 18 0
STAAT Zeichen 18 0
LAND Zeichen 24 0
PLZ Zeichen 10 0
TELEFON Zeichen 14 0
────────────────────────────────────────────────────────────────────────────────
I - Allgemeine DateiInfos * S - Struktur der Datenbank * D - Datensatznummer
N - Nächster Datensatz * V - Vorheriger Datensatz * A - Andere Datei * E - Ende
Nach den Deklarationen wird die Prozedur »HoleDatei« aufgerufen, die vom Benutzer einen Dateinamen erfragt (ab Zeile 140). Die Datei wird, falls vorhanden, als Binärdatei geöffnet. Wenn dabei ein Fehler auftritt, verzweigt das Programm in die Fehlerroutine mit der Marke »FehlerProz« (ab Zeile 112). Anschließend wird geprüft, ob die Datei von ihrer Länge her eine dBase-Datei sein kann. Sofern dies der Fall ist, werden mit dem Befehl
GET #1, 1, Head
die allgemeinen Dateiinformationen des Vorspanns in die Variable vom Datentyp »Header« gespeichert. Dann werden – wegen der Komplexität der Bedingung in zwei Zeilen – die Vorspanninformationen auf Plausibilität geprüft. Diese Fehlerprüfung könnte noch engmaschiger ausfallen, wenn die Vorspanninformationen auf interne Stimmigkeit untersucht würden – darauf wurde aus Platzgründen verzichtet. Nach diesen Fehlerprüfungen werden die Feldinformationen in das (mit Hilfe der Vorspanninformationen neu dimensionierte) Array vom Typ »Satz Struktur« eingelesen. Die Prozedur »ZeigeVorspann« (ab Zeile 262) wertet die allgemeinen Dateiinformationen, die Prozedur »ZeigeStruktur« (ab Zeile 234) die Feldinformationen des Dateivorspanns aus. Die Prozedur »SatzAnzeige« (ab Zeile 187) dient der Anzeige der Datensätze, wobei für lange Felder ein Zeilenumbruch vorgesehen ist. Die beiden letztgenannten Prozeduren geben die Nummer des zuletzt angezeigten Feldes an das Hauptprogramm zurück, falls die Informationen nicht auf eine Bildschirmseite passen. Von den Werten der Variablen »FeldAngez%« beziehungsweise »FeldFertig%« hängt es dabei ab, ob der Benutzer mit der Leertaste weitere Informationen abfragen kann. Bevor die Prozedur »SatzAnzeige« aufgerufen wird, muß die Position, an der sich die Datensatzinformationen befinden, errechnet und mit der Seek-Anweisung festgelegt werden. In die Berechnung fließen die Vorspannlänge, die Nummer des anzuzeigenden Datensatzes und die Datensatzlänge ein. Die Prozedur »Ende« schließt die dBase-Datei und beendet das Programm.
(Karl-Ernst Prankel/al)
'* --------------------------------------*
'* Prog.-Name: DBQUICK.BAS *
'* Anzeige von Informationen über *
'* dBase-Datenbankdateien *
'* Prog.-Sprache: Quick-Basic 4.5 *
'* alle Grafikkarten *
'* (c) Redaktionsbüro Everts&Hagedorn *
'* Autor: Karl-Ernst Prankel *
'* --------------------------------------*
CONST FALSCH = 0, WAHR = NOT FALSCH
TYPE Header
Version AS STRING * 1
Jahr AS STRING * 1
Monat AS STRING * 1
Tag AS STRING * 1
AnzSaetze AS LONG
LenVorspann AS INTEGER
LenSatz AS INTEGER
Dummy1 AS INTEGER
Transakt AS STRING * 1
Kodiert AS STRING * 1
Dummy2 AS STRING * 12
Mdx AS STRING * 1
Dummy3 AS STRING * 3
END TYPE
TYPE SatzStrukt
Feldname AS STRING * 11
FeldTyp AS STRING * 1
Dummy01 AS STRING * 4
LenFeld AS STRING * 1
LenDezStelle AS STRING * 1
Dummy02 AS STRING * 2
ArbeitsBer AS STRING * 1
Dummy03 AS STRING * 11
END TYPE
DECLARE SUB Ende ()
DECLARE SUB HoleDatei (Head AS ANY, Felder() AS ANY)
DECLARE SUB SatzAnzeige (FeldAngez%, Nr&, Felder() AS ANY)
DECLARE SUB ZeigeStruktur (FeldFertig%, Struktur() AS ANY)
DECLARE SUB ZeigeVorspann (Head AS ANY)
DIM SHARED Fehler
DIM Kopf AS Header
REM $DYNAMIC
DIM FeldInfo(1) AS SatzStrukt
CALL HoleDatei(Kopf, FeldInfo())
DO
VIEW PRINT
LOCATE 24: PRINT "I - Allgemeine DateiInfos * S - Struktur der Datenbank * D - Datensatznummer"
LOCATE 25: PRINT "N - Nächster Datensatz * V - Vorheriger Datensatz * A - Andere Datei * E - Ende";
DO
Eingabe$ = UCASE$(INPUT$(1))
LOOP UNTIL INSTR("ADEINSV ", Eingabe$) <> 0
LOCATE 22: PRINT STRING$(80, 196);
IF Eingabe$ <> " " THEN FeldAngez% = 0: FeldFertig% = 1
SELECT CASE Eingabe$
CASE "A"
CALL HoleDatei(Kopf, FeldInfo())
SatzNummer& = 0
CASE "E"
CALL Ende
CASE "I"
CALL ZeigeVorspann(Kopf)
CASE "S"
CALL ZeigeStruktur(FeldFertig%, FeldInfo())
CASE "D"
IF Kopf.AnzSaetze <> 0 THEN
LOCATE 23: PRINT SPACE$(80);
DO
LOCATE 24: PRINT SPACE$(80); : LOCATE 24
PRINT "Geben Sie eine Datensatznummer zwischen 1 und" + STR$(Kopf.AnzSaetze) + " ein: ";
LINE INPUT ; "", SatzNr$
SatzNummer& = VAL(SatzNr$)
IF SatzNummer& >= 1 AND SatzNummer& <= (Kopf.AnzSaetze) THEN
EXIT DO
ELSE
LOCATE 23: PRINT SPACE$(14) + "EINGEGEBENE DATENSATZNUMMER AUSSERHALB DES BEREICHS!";
END IF
LOOP
END IF
CASE "N"
IF SatzNummer& < Kopf.AnzSaetze THEN SatzNummer& = SatzNummer& + 1
CASE "V"
IF SatzNummer& > 1 THEN SatzNummer& = SatzNummer& - 1 ELSE SatzNummer& = 1
CASE " "
IF FeldAngez% > 0 AND FeldAngez% < UBOUND(FeldInfo) THEN
SEEK #1, SEEK(1) - ASC(FeldInfo(FeldAngez% + 1).LenFeld)
CALL SatzAnzeige(FeldAngez%, SatzNummer&, FeldInfo())
END IF
IF FeldFertig% > 1 AND FeldFertig% < UBOUND(FeldInfo) THEN
CALL ZeigeStruktur(FeldFertig%, FeldInfo())
END IF
END SELECT
IF INSTR("DNV", Eingabe$) <> 0 THEN
IF Kopf.AnzSaetze <> 0 AND Kopf.LenVorspann + (Kopf.LenSatz * SatzNummer&) <= LOF(1) THEN
SEEK #1, Kopf.LenVorspann + 1 + ((SatzNummer& - 1) * Kopf.LenSatz)
CALL SatzAnzeige(FeldAngez%, SatzNummer&, FeldInfo())
ELSE
VIEW PRINT 5 TO 22
CLS 2
LOCATE 12, 5
IF Kopf.AnzSaetze = 0 THEN
PRINT "Die Datenbank ist leer!"
ELSE
PRINT "Die Angaben im Dateivorspann stimmen nicht mit der Dateigröße überein!": LOCATE , 5
PRINT "Mehr als"; : PRINT (LOF(1) - Kopf.LenVorspann) \ Kopf.LenSatz; : PRINT "Datensätze passen nicht in die Datei!"
SatzNummer& = ((LOF(1) - Kopf.LenVorspann) \ Kopf.LenSatz) + 1
END IF
END IF
END IF
LOOP
END
FehlerProz:
LOCATE 13, 15
SELECT CASE ERR
CASE 55
CLOSE #1
RESUME
CASE 64
PRINT "Unzulässiger Dateiname!"
CASE 71
PRINT "Diskette nicht bereit!"
CASE 76
PRINT "Pfad nicht gefunden!"
CASE ELSE
VIEW PRINT 9 TO 15
CLS
ON ERROR GOTO 0
END SELECT
LOCATE , 15: PRINT "Betätigen Sie eine beliebige Taste!"
Eing$ = INPUT$(1)
Fehler = WAHR
RESUME NEXT
REM $STATIC
SUB Ende
VIEW PRINT
CLS
CLOSE #1
END
END SUB
SUB HoleDatei (Head AS Header, Felder() AS SatzStrukt) STATIC
DO
Fehler = FALSCH
CLS
PRINT SPACE$(10) + "Informationen über dBASE-Datenbankdateien unter QUICK BASIC" + SPACE$(10)
LOCATE 4: PRINT STRING$(80, 196)
LOCATE 23: PRINT STRING$(80, 196)
LOCATE 24, 1: PRINT "Geben Sie [Laufwerk, Pfad] Namen [und Extension] einer dBASE -Datei ein!";
LOCATE 25, 1: INPUT ; "[<ENTER> allein beendet das Programm]: ", Dateiname$
IF Dateiname$ = "" THEN CALL Ende
IF INSTR(Dateiname$, ".") = 0 THEN Dateiname$ = Dateiname$ + ".dbf"
LOCATE 2, (82 - LEN(Dateiname$)) \ 2: PRINT UCASE$(Dateiname$)
ON ERROR GOTO FehlerProz
OPEN Dateiname$ FOR BINARY AS #1
ON ERROR GOTO 0
IF NOT Fehler THEN
IF LOF(1) < 66 THEN
IF LOF(1) = 0 THEN
LOCATE 10, 15: PRINT "Die Datei " + Dateiname$
LOCATE , 15: PRINT "hat eine Länge von 0 Byte!"
LOCATE 13, 15: PRINT "Betätigen Sie die Taste <J>, um die Datei zu löschen,"
LOCATE , 15: PRINT "oder eine beliebige andere Taste!"
IF UCASE$(INPUT$(1)) = "J" THEN CLOSE #1: KILL Dateiname$
ELSE
LOCATE 11, 15: PRINT "Die Datei " + Dateiname$
LOCATE , 15: PRINT "kann von ihrer Länge her keine dBASE-Datei sein."
LOCATE , 15: PRINT "Betätigen Sie eine beliebige Taste!": Eing$ = INPUT$(1)
END IF
Fehler = WAHR
ELSE
GET #1, 1, Head
IF ASC(Head.Version) MOD 8 <> 3 OR ASC(Head.Jahr) > 99 OR ASC(Head.Monat) = 0 OR ASC(Head.Monat) > 12 OR ASC(Head.Tag) = 0 OR ASC(Head.Tag) > 31 OR Head.AnzSaetze > 1000000000 OR Head.AnzSaetze < 0 THEN Fehler = WAHR
IF Head.LenVorspann < 65 OR Head.LenVorspann > 8193 OR Head.LenSatz < 2 OR Head.LenSatz > 4000 OR ASC(Head.Transakt) > 1 OR ASC(Head.Kodiert) > 1 OR ASC(Head.Mdx) > 1 THEN Fehler = WAHR
IF Fehler THEN
LOCATE 10, 15: PRINT "Die gewählte Datei ist keine dBASE-Datei oder"
LOCATE 11, 15: PRINT "sie enthält unzulässige Werte im Datei-Vorspann!"
LOCATE 13, 15: PRINT "Betätigen Sie eine beliebige Taste!": Eing$ = INPUT$(1)
END IF
END IF
END IF
LOOP WHILE Fehler
REDIM Felder((Head.LenVorspann - 33) \ 32) AS SatzStrukt
FOR z% = 1 TO UBOUND(Felder)
GET #1, , Felder(z%)
NEXT z%
CALL ZeigeVorspann(Head)
END SUB
SUB SatzAnzeige (FeldAngez%, Nr&, Felder() AS SatzStrukt) STATIC
VIEW PRINT
SatzNrAnzeige$ = "Anzeige von Datensatz Nummer:" + STR$(Nr&)
LOCATE 3: PRINT SPACE$((80 - LEN(SatzNrAnzeige$)) \ 2) + SatzNrAnzeige$ + SPACE$((80 - LEN(SatzNrAnzeige$)) \ 2)
zeile% = 1
IF FeldAngez% = 0 THEN
IF INPUT$(1, #1) = "*" THEN
LOCATE 4, 24: PRINT " DATENSATZ ZUM LÖSCHEN MARKIERT! "
ELSE
PRINT STRING$(80, 196)
END IF
END IF
VIEW PRINT 5 TO 22
CLS 2
FOR z% = FeldAngez% + 1 TO UBOUND(Felder)
zeichenket$ = INPUT$(ASC(Felder(z%).LenFeld), #1)
PRINT MID$(Felder(z%).Feldname, 1, INSTR(Felder(z%).Feldname, CHR$(0)) - 1); : LOCATE , 11: PRINT ":";
DO WHILE LEN(zeichenket$) > 0
IF LEN(zeichenket$) > 65 THEN
Position% = 65
DO WHILE MID$(zeichenket$, Position%, 1) <> " "
Position% = Position% - 1
IF Position% = 1 THEN Position% = 65: EXIT DO
LOOP
LOCATE , 15: PRINT LEFT$(zeichenket$, Position%);
zeichenket$ = RIGHT$(zeichenket$, LEN(zeichenket$) - Position%)
ELSE
LOCATE , 15
IF Felder(z%).FeldTyp = "D" THEN
PRINT RIGHT$(zeichenket$, 2) + "." + MID$(zeichenket$, 5, 2) + "." + LEFT$(zeichenket$, 4);
ELSE
PRINT zeichenket$;
END IF
zeichenket$ = ""
END IF
zeile% = zeile% + 1
IF zeile% = 19 AND (LEN(zeichenket$) > 0 OR z% < UBOUND(Felder)) THEN
VIEW PRINT
LOCATE 23, 17: PRINT " Leertaste - Fortsetzung der Anzeige des Satzes ";
z% = z% - 1
EXIT FOR
END IF
IF zeile% <> 19 THEN PRINT
LOOP
NEXT z%
FeldAngez% = z%
END SUB
SUB ZeigeStruktur (FeldFertig%, Struktur() AS SatzStrukt) STATIC
VIEW PRINT
LOCATE 3: PRINT SPACE$(20) + "Informationen über die Datensatzstruktur" + SPACE$(20)
PRINT STRING$(80, 196)
PRINT "Feldname Feldtyp Feldlänge Dezimalstellen" + SPACE$(22)
PRINT STRING$(80, "-")
VIEW PRINT 7 TO 22
CLS 2
FOR z% = FeldFertig% TO UBOUND(Struktur)
SELECT CASE Struktur(z%).FeldTyp
CASE "C": Typ$ = "Zeichen"
CASE "N": Typ$ = "Numerisch"
CASE "D": Typ$ = "Datum"
CASE "L": Typ$ = "Logisch"
CASE "F": Typ$ = "Gleitkomma"
CASE "M": Typ$ = "Memo"
CASE ELSE: Typ$ = "unbekannt"
END SELECT
PRINT MID$(Struktur(z%).Feldname, 1, INSTR(Struktur(z%).Feldname, CHR$(0))), Typ$, ASC(Struktur(z%).LenFeld), ASC(Struktur(z%).LenDezStelle);
IF z% MOD 16 = 0 AND z% < UBOUND(Struktur) THEN
VIEW PRINT
LOCATE 23, 12: PRINT " Leertaste - Fortsetzung der Anzeige der Datensatzstruktur ";
EXIT FOR
END IF
IF z% MOD 16 <> 0 THEN PRINT
NEXT z%
FeldFertig% = z% + 1
END SUB
SUB ZeigeVorspann (Head AS Header) STATIC
VIEW PRINT
LOCATE 3: PRINT SPACE$(25) + "Allgemeine Dateiinformationen" + SPACE$(25)
LOCATE 23: PRINT STRING$(80, 196);
VIEW PRINT 5 TO 22
CLS 2
LOCATE 8: PRINT "Version:"; : LOCATE , 35
SELECT CASE ASC(Head.Version)
CASE 3: PRINT "dBASE III Plus / dBASE IV"
CASE 131: PRINT "dBASE III Plus"
CASE ELSE: PRINT "dBASE IV"
END SELECT
PRINT "Memodatei:"; : LOCATE , 35
IF ASC(Head.Version) > 130 THEN PRINT "vorhanden" ELSE PRINT "nicht vorhanden"
PRINT "Datum der letzten Änderung:"; : LOCATE , 35
PRINT LTRIM$(STR$(ASC(Head.Tag))); "."; LTRIM$(STR$(ASC(Head.Monat))); ".";
IF ASC(Head.Jahr) < 9 THEN PRINT "0";
PRINT LTRIM$(STR$(ASC(Head.Jahr)))
PRINT "Anzahl der Datensätze:"; : LOCATE , 34: PRINT Head.AnzSaetze
PRINT "Anzahl der Felder:"; : LOCATE , 34: PRINT (Head.LenVorspann - 33) \ 32
PRINT "Länge der Datensätze in Byte:"; : LOCATE , 34: PRINT Head.LenSatz; : PRINT " (incl. Löschkennzeichen)"
PRINT "Länge des Vorspanns in Byte:"; : LOCATE , 34: PRINT Head.LenVorspann
IF NOT ASC(Head.Version) = 131 THEN
PRINT "Transaktionsbit:"; : LOCATE , 35
IF ASC(Head.Transakt) = 0 THEN PRINT "nicht ";
PRINT "gesetzt"
PRINT "Datenbank:"; : LOCATE , 35
IF ASC(Head.Kodiert) = 0 THEN PRINT "nicht ";
PRINT "kodiert"
PRINT "MDX-Indexdatei:"; : LOCATE , 35
IF ASC(Head.Mdx) = 0 THEN PRINT "nicht ";
PRINT "vorhanden"
END IF
PRINT "Dateilänge in Byte:"; : LOCATE , 34: PRINT LOF(1)
PRINT
END SUB
aus DOS International 11/90 – Seite 66-73