Rob´s BB-Kurs 2

Mit Dateien arbeiten


© Robert Gerlach 2001

www.robsite.de



Eigentlich ist der Titel Mit Dateien arbeiten etwas ungenau, denn bis jetzt haben wir schon ziemlich oft mit Dateien gearbeitet, nämlich indem wir Bilder und Sounddateien geladen haben. In diesem Kapitel geht es allerdings um die Grundlagen, um Manipulation von einzelnen Daten in Dateien. Am Anfang gehts erstmal nur um Textdateien, da sie am einfachsten zu bearbeiten sind. Nun denn, öffnen wir erstmal eine Datei.


OpenFile kann nur Dateien öffnen die bereits existieren. Die Datei wird also nicht extra erstellt wenn sie nicht existiert.
Gut, öffnen wir also erstmal eine ASCII-.txt Datei. Sie sollte möglichst schon existieren, sonst gibts einen Fehler...:


stream = OpenFile("bopp.txt")


Die Datei-Variable heisst stream ("Datenfluß") weil wir soeben einen Datenstream zu einer Datei geöffnet haben. Immer wenn man eine Verbindung zu einer Datei auf der Festplatte, einem Server u.s.w. aufbaut ist das so als wenn man... einen Fluß (Stream) zwischen dem eigenen Programm und dem geöffneten Gerät/Gebiet auf der Festplatte... eingerichtet hat auf dem dann die Daten hin und her geschickt werden können.
Gut. Nun haben wir Lese- und Schreibzugriff auf die Datei bopp.text. Wollen wir zufällig nur Lesezugriff oder nur Schreibzugriff haben, können wir selbiges erreichen indem wir die Datei anders öffnen. ReadFile kann man auch nur auf schon bestehende Dateien anwenden, nur WriteFile erstellt eine wenn sie nicht existiert.


; Datei nur zum schreiben öffnen:
stream = WriteFile("bopp.txt)


; Datei nur zum lesen öffnen:
stream = ReadFile("bopp.txt)


Im Moment ist aber erstmal WriteFile das beste, wir brauchen uns immerhin keine Gedanken darüber zu machen ob die Datei jetzt existiert oder nicht. Schreiben wir also etwas.

Es gibt viele Möglichkeiten Daten in Dateien abzulegen. Man kann einzelne Bytes speichern, aber auch Integerzahlen (die aus 4 Bytes bestehen), Shortzahlen, Strings, Floatzahlen u.s.w. Man sollte für jede Aufgabe möglichst immer das kleinstmögliche nehmen. Speichert man z.B. nur Buchstaben nimmt man einzelne Bytes. Warum? Weil ein Byte die Werte 0-255 annehmen kann und die ASCII-Werte eines Buchstaben ebenfalls von 0-255 gehen. Ideal also.
Um nun ein Byte beliebigen Wertes in der Datei bopp.txt zu speichern, schreiben wir folgendes:


stream = WriteFile("bopp.txt")

WriteByte stream, 82


Öffnen wir jetzt die eben erstellte Datei mit einem Texteditor (Notepad), müsste ein großes R drinstehen, denn der ASCII-Wert von R ist 82. Nun kann man sich aber nicht ohne größeren Zeitaufwand alle 255 ASCII-Zeichen merken, weshalb es den Asc() Befehl gibt. In den Klammern steht das Zeichen und rauskommt sein ASCII-Code. Ein R kann man also auch so in eine Datei schreiben:


WriteByte stream, Asc("R")


Schon gleich viel besser. Wer zufällig auch mal alle Zeichen die es so gibt in einer Datei haben will, führe folgenden Code aus:


stream = WriteFile("alle_ascii_zeichen.txt")

For ascii = 1 To 255
  WriteByte stream, ascii
Next


Das aber nur nebenbei. Weiter mit anderen Datenarten. Die Befehle dazu sind sich alle sehr ähnlich, nur die Byte-Werte der Typen sind unterschiedlich. Hier eine kleine Tabelle dazu:


Befehl
Name
Bytes
Typ
Von
Bis
WriteByte stream, wert
WriteShort stream, wert
WriteInt stream, wert
WriteFloat stream, wert#
WriteLine stream, wert$
WriteString stream, wert$
Byte
Short
Integer
Float
Line
String
1
2
4
4
0-x
4-x
Ganzzahl
Ganzzahl
Ganzzahl
Kommazahl
Text
Text
0
-32768
-2147483648
-2 Mrd.
0
4
255
32767
2147483647
2 Mrd.
x
x


WriteByte haben wir eben kennengelernt, die nächst größere Einheit ist eine Short-Zahl, sie ist sozusagen der kleine Ableger einer Integerzahl denn sie hat nur halb soviel Bytes wie sie und kann somit nur begrenzt große Zahlen darstellen.
Integerzahlen mit 4 Bytes werden wohl am häufigsten verwendet, auch wenn man oft nie über die 32768-Zahlengrenze der Shortzahlen kommt. Es ist zwar heute nicht mehr so wichtig ob man 2 statt 4 Bytes irgendwo auf der riesigen Festplatte gespeichert hat, nur wenn man sie wieder in den, viel kleineren, Arbeitsspeicher lädt können um die Hälfte kleinere Zahlen doch schon eine Menge ausmachen. Wenn es sehr viele sind...
Will man dagegen exakte Kommazahlen abspeichern, tut man dies mit WriteFloat, Floatzahlen sind auch 4 Bytes groß.
Das wars mit den Zahlen, was folgt sind die Strings, also Text. Man kann Strings in 2 Arten speichern, Zeilen- und Zeichenweise. Um eine Zeile voll mit ASCII-Zeichen zu speichern, nimmt man WriteLine datei, Text$, man kann dann beliebig viele Zeichen im Text-String abspeichern. Damit man nachher beim auslesen erkennt das am Ende das Zeilenende ist fügt Blitz noch ein Returnzeichen (0dh <-- Hexadezimalzahl) ein. Will man also längere Texte oder Zeichenketten in eine Datei schreiben, nehme man WriteLine. WriteString macht so ziemlich das gleiche, nur dass bei ihm noch vor dem Text eine Integerzahl steht die die Länge der Nachfolgenden Zeichenkette angibt. Somit ist ein WriteString-String minimal 4 Bytes groß, ein mit WriteLine abgespeicherter minimal 0 (praktisch wenn man nur eine Leerzeile ohne Speicherverbrauch haben will).
Ein kleines Beispielprogramm was alles mögliche in einer Datei speichert:


; Datei zum schreiben öffen, Stream anlegen.
datei = WriteFile("bopp.txt")

; Ein Byte speichern
WriteByte datei, 78

; Ein Byte mit Hilfe von Asc() speichern.
WriteByte datei, Asc("D")

; Eine Shortzahl speichern:
WriteShort datei, 23

; Eine Integerzahl speichern:
WriteInt datei, 5

; Eine Floatzahl speichern:
WriteFloat datei, 666.235

; Eine Zeile Speichern:
WriteLine datei, "Preisfrage: Würde Toblerone schmelzen"

; Einen String-Datensatz speichern:
WriteString datei, "wenn man sie in die Super-ILLU einpacken würde? Wer weiß..."

; Die Datei wieder schließen.
CloseFile datei


Der letzte Befehl (CloseFile)ist wichtig, er schließt die Datei nämlich wieder, trennt also den Stream. Das sollte man möglichst nie vergessen...

Öffnet man jetzt die durch das Programm erstellte Datei wird man ein ziemlich chaotisches Bild vorfinden. Notepad kann anscheinend nur ASCII-Text, also einzelne Bytes, lesen und anzeigen, bei Short-, Integer- und Floatzahlen macht es schlapp. Nur mit den richtigen Methoden/Befehlen können wir z.B. eine mit WriteShort in eine Datei geschriebene Zahl wieder auslesen und anzeigen.
Die Befehle zum auslesen von Werten unterscheiden sich nicht sehr von denen zum schreiben, nur dass sie... umgekehrt funktionieren...:


; Um ein Byte auszulesen:
wert = ReadByte(stream)

; Um eine Shortzahl auszulesen:
wert = ReadShort(stream)

; Um eine Integerzahl auszulesen:
wert = ReadInt(stream)

; Um eine Floatzahl auszulesen:
wert# = ReadFloat(stream)

; Um eine Zeile auszulesen:
wert$ = ReadLine$(stream)

; Um einen String auszulesen:
wert$ = ReadString$(stream)


Man muss einen mit einem bestimmten Befehl abgespeicherten Wert immer genau mit seinem Lese-Gegenstück auslesen. Also ein Byte mit ReadByte und nicht mit ReadFloat u.s.w.

Nun können wir in Dateien schreiben und das ganze auch noch auslesen. Bevor es weitergeht mit Methoden wie man das ganze am besten macht, noch ein paar nützliche Befehle rund um Dateien.
Weiß man nicht ob eine Datei überhaupt existiert, kann man das folgendermaßen prüfen:


rueckgabe = FileType("dateiname.dat")


rueckgabe hat danach folgende Werte:
0 - Die Datei existiert nicht.
1 - Die Datei existiert.
2 - Es ist keine Datei sondern ein Verzeichnis ^_^

So kann man mit FileType also auch noch prüfen ob ein bestimmtes Verzeichnis existiert. Sehr praktisch.



Will man nun z.B. die Größe einer Datei haben, nehme man FileSize:


groeße = FileSize("dateiname.dat")


Es wird dann die Größe in Bytes zurückgegeben. Nochmal zur Erinnerung:
8 Bit = 1 Byte
1024 Byte = 1 KiloByte
1024 KiloByte = 1 MegaByte
1024 MegaByte = 1 GigaByte
u.s.w.

So kann man sich auch einfach eine kleine Function schreiben die die Größe in KB zurückgibt:


Function DateiKB#(datei$)
groeße# = FileSize(datei$) / 1024
Return groeße#
End Function


Das aber nur nebenbei...

Nun noch kurz zwei Standardbefehle die man immer mal braucht:


; Um eine Datei zu kopieren:
CopyFile quellpfad$, zielpfad$


; Um eine Datei zu löschen:
DeleteFile dateipfad$


Und um sich in Verzeichnissen/Ordnern zu bewegen oder selbige zu löschen gibt es noch folgende Befehle:


; Um ein neues Verzeichnis zu erstellen:
CreateDir Verzeichnispfad$


; Um herauszufinden in welchem Verzeichnis man sich gerade befindet:
Verzeichnispfad$ = CurrentDir()


; Falls man in ein anderes Verzeichnis wechseln will:
ChangeDir Neuer_Verzeichnispfad$


; Um ein Verzeichnis zu löschen nehme man:
DeleteDir Verzeichnispfad$


Man kann aber auch noch mehr mit Verzeichnissen machen, z.B. alle Dateien in ihnen auswählen.
Dazu muss man erstmal das Verzeichnis seiner Wahl öffnen:


Verzeichnisvariable = ReadDir(Verzeichnispfad$)


Nun kann amn in diesem Verzeichnis Dateien auswählen:


Dateipfad$ = NextFile(Verzeichnisvariable)


Mit NextFile geht man also immer eine Datei in dem Verzeichnis weiter. Damit kann man sich z.B. eine DIR Funktion wie in MS-DOS schreiben, welche alle Dateien und Unterverzeichnisse in einem Ordner auflistet. Hier eine kleine DIR-Funktion:


Function dir(verzeichnis$)
ordner = ReadDir(verzeichnis$) ; Verzeichnis öffnen.

; Schleife in der das ganze geprüft und ausgegeben wird.
Repeat
datei$ = NextFile(ordner) ; Neue Datei auswählen.
; Überprüfen ob sie eine Datei oder ein Verzeichnis ist und dementsprechend... was anderes hinschreiben.
If FileType(verzeichnis$ + datei$) = 2 Then
Print datei$ + " <DIR>"
Else
Print datei$
EndIf
Until datei$ = ""

CloseDir ordner ; Verzeichnis schließen.
End Function


Einfach dir("C:\") eingeben und sehen was so alles im C:\ Verzeichnis rumlungert.
CloseDir schließt ein Verzeichnis übrigens wieder...


Und nun zu ein paar kleinen Beispielen zur praktischen Anwendung von dem ganzen, komplett mit ein paar nützlichen Functions.

Will man z.B. ein Array (DIM-Feld) in einer Datei schreiben und wieder auslesen, kann man das z.B. so machen:


; Ein Feld erstellen.
Dim feld(10,10)

; Zufallswerte erzeugen
For x = 1 To 10
For y = 1 To 10
feld(x,y) = Rnd(1,10)
Next
Next

; Eine Datei zum schreiben öffnen.
datei = WriteFile("feld-test.txt")

; Die Werte des Feldes in der Datei speichern. Mit Writebyte da die Werte nicht über 255 gehen.
For x = 1 To 10
For y = 1 To 10
WriteByte datei, feld(x,y)
Next
Next

; Die Datei schließen.
CloseFile datei


Öffnet man nun diese Datei sieht man wahrscheinlich viele kleine Kästchen, wir haben ja auch keine Buchstaben gespeichert. Um nun das ganze wieder in ein neues Feld einzulesen können wir folgendes machen:


; Neues Feld erstellen:
Dim neuesfeld(10,10)

; Die eben erstellte Datei wieder öffnen:
datei = ReadFile("feld-test.txt")

; Die Werte in das Feld einlesen und ausgeben:
For x = 1 To 10
For y = 1 To 10
neuesfeld(x,y) = ReadByte(datei)
Print neuesfeld(x,y)
Next
Next

; Die Datei wieder schließen:
CloseFile datei


Das war einfach. Was aber wenn man z.B. eine Datei mit diesem Aufbau hat:


23,5,19,49
94,9,145,32
12,529,73,9
u.s.w.


Also wenn in einer Zeile jeweils 4 unterschiedlich lange Zahlen durch Kommas getrennt gespeichert sind? Dann sollte man die Datei Zeile für Zeile (ReadLine) auslesen und die Zahlen aus dem String extrahieren. Dies ist etwas komplizierter, normalerweise. Man kann aber auch meine bewährte ZahlAusString-Function nehmen ^_^. Um das Beispiel auszuprobieren solltest du am besten eine Datei zeilenzahlen.txt erstellen die in jeder Zeile 4 durch Kommas getrennte Zahlen hat. Die Zeilenanzahl ist egal.


; Eine Datei öffnen.
datei = ReadFile("zeilenzahlen.txt")

; Schleife solange wiederholen bis das Ende (Eof --> End of File) der Datei erreicht ist.
While Not Eof(datei)

; Eine Zeile auslesen:
zeile$ = ReadLine(datei)

; Jeweils die erste bis vierte Zahl extrahieren und in eine eigene Variable speichern.
zahl1 = zahlausstring(zeile$, 1)
zahl2 = zahlausstring(zeile$, 2)
zahl3 = zahlausstring(zeile$, 3)
zahl4 = zahlausstring(zeile$, 4)

; Die Zahlen ausgeben.
Print zahl1 + " " + zahl2 + " " + zahl3 + " " + zahl4

Wend



; Die Function die die ganze Arbeit macht...
;------------------------------------------------------
; zahlstring$ ist der String in dem die Zahlen stehen, stelle ist die Stelle der Zahl. Wenn stelle = 3 ist wird also die dritte Zahl zurückgegeben.
; Muss man nicht verstehen, nur benutzen können ^_^.
Function zahlausstring(zahlstring$, stelle)
anzahl = 1
letzteskomma = 1
For i = 1 To Len(zahlstring$)
If Mid(zahlstring$, i, 1) = "," Or i = Len(zahlstring$) Then
If anzahl = stelle Then zahl = Mid(zahlstring$, letzteskomma, i)
letzteskomma = i+1
anzahl = anzahl + 1
EndIf
Next
Return zahl
End Function


Nun können wir schon so ziemlich alles mit Dateien machen was man so brauchen könnte. Ein paar Sachen fehlen noch, diese sind aber nicht unbedingt notwendig... Außerdem ist diese Kapitel schon jetzt viel zu groß ^_^.
Wer die restlichen 2 Befehle haben will lese die Command Reference oder gehe auf www.blitzbase.de.






Zusammenfassung


; Eine bestehende Date zum lesen und schreiben öffnen:
stream = OpenFile(dateipfad$)


; Eine bestehende Datei nur zum lesen öffnen:
stream = ReadFile(dateipfad$)


; Um eine Datei nur zum schreiben zu öffnen oder sie gleich dazu zu erstellen:
stream = WriteFile(dateipfad$)


; Die Befehle um verschiedene Datentypen in eine geöffnete Datei zu schreiben:
WriteByte stream, wert
WriteByte stream, wert
WriteInt stream, wert
WriteFloat stream, wert#
WriteLine stream, wert$
WriteString stream, wert$


; Und die Befehle um verschiedene Datentypen aus einer geöffneten Datei zu lesen:
wert = ReadByte(stream)
wert = ReadShort(stream)
wert = ReadInt(stream)
wert# = ReadFloat(stream)
wert$ = ReadLine(stream)
wert$ = ReadString(stream)


; Um herauszubekommen ob eine Datei existiert und wenn ja ob es überhaupt eine Datei oder ein Verzeichnis ist:
rueckgabe = FileType(dateipfad$)
; rueckgabe hat dann folgende Werte:
; 0 = Datei/Ordner existiert nicht.
; 1 = Datei existiert.
; 2 = Es ist keine Datei sondern ein Ordner.


; Um die Größe einer Datei in Bytes zu bekommen:
groesse = FileSize(dateipfad$)


; Um eine Datei zu kopieren:
CopyFile quellpfad$, zielpfad$


; Um eine Datei zu löschen:
DeleteFile dateipfad$


; Um ein neues Verzeichnis zu erstellen:
CreateDir Verzeichnispfad$


; Um herauszufinden in welchem Verzeichnis man sich gerade befindet:
Verzeichnispfad$ = CurrentDir()


; Falls man in ein anderes Verzeichnis wechseln will:
ChangeDir Neuer_Verzeichnispfad$


; Um ein Verzeichnis zu löschen nehme man:
DeleteDir Verzeichnispfad$


; Um ein Verzeichnis zu öffnen um die Dateien und Unterverzeichnisse in ihm auswählen zu können:
Verzeichnisvariable = ReadDir(Verzeichnispfad$)


; Um eine Datei aus einem geöffneten Verzeichnis auswählen zu können (und um immer eine Datei weiter in dem Verzeichnis zu gehen):
Dateipfad$ = NextFile(Verzeichnisvariable)


Nun zum letzten und schwersten Teil des Blitz Basic Kurses, Multiplayer- und Netzwerkbefehle nebst Methoden wie man selbige am besten anwendet.