Hallo o/
Aus verschiedenen Gründen brauchte ich ein Dateiformat, welches Hauptsächlich Konfigurationen speichert. Mein erster Gedanke war logischerweise eine simple *.ini Datei. Jedoch war das Format leider noch viel zu unflexibel für mein Vorhaben. Andere Dateiformate waren und sind auch viel zu kompliziert (z.B. JSON) um schnell mal einen Parser dafür zu schreiben. Also hatte ich mir überlegt, wie kann man ein Dateiformat so simpel wie möglich halten und dennoch sämtliche Möglichkeiten offen halten?
Lustigerweise hatte ich dann auch eine Idee. Der Parser dazu war auch schnell geschrieben (obwohl dieser sicherlich mit String Funktionen um einiges schneller wäre). Ich nenne das Format Yjuq's File Format oder kurz: YFF - *.yff
Das Prinzip dieses Formates ist extrem simpel. Du wählst dir dein Delimeter und Escape Zeichen aus. Man kann sich auch mehrere aussuchen. Zu einfachen Demonstationszwecken benutze ich folgende:
Delimeter: < und >
Escape: \
Alles was zwischen den Delimeter ist wird als Datensatz gewertet, alles was direkt nach einem Escape Zeichen in einem Datensatz kommt, wird ebenfalls als Datensatz gewertet. Das bedeutet dass die Escape Zeichen hauptsächlich dazu dienen, ebenfalls die Delimeter als Datensatz zu erkennen. Logischerweise kannst du damit auch Escape Zeichen als Datensatz werten lassen. Alles andere, was nicht zwischen Delimeter steht, wird schlichtweg ignoriert.
Dieses Dateiformat erlaubt es das Dateiformat frei zu bestimmen und dennoch die Datensätze zu deklarieren. Die ignorierten Zeichen können als Kommentare genutzt werden oder für weitere Datensätze für andere Delimeter und Escape Einstellungen. Da bleibt es eurer Fantasie überlassen.
--------------------------------------------------------------------------------
Das ist ein Beispiel für ein valides Dokument. Da alle Zeichen bis auf die
Zeichen zwischen [Delimeter] ignoriert werden, kann ich hier schlichtweg den
Platz für Dokumentationszwecke nutzen.
--------------------------------------------------------------------------------
Hier ein Beispiel für ein Datensatz: <Ich bin ein Datensatz>
Da "Ich bin ein Datensatz" zwischen 2 Delimeter geschrieben wurde, wird dies als
gültiger Datensatz erkannt.
--------------------------------------------------------------------------------
Hier ein weiteres Beispiel: <\<Daten\>>
Dies würde euch den Datensatz "<Daten>" ausgeben.
^^^^^
Da dies nicht escaped wurde, ist das auch ein Datensatz ^^
--------------------------------------------------------------------------------
In: <data<
Out: "data"
In: >data>
Out: "data"
In: <da\ta>
Out: "data"
In: <da\\ta>
Out: "da\ta"
--------------------------------------------------------------------------------
Ich denke das Prinzip ist klar.
Alles anzeigen
Hier einmal ein Beispielskript mit dem Parser dazu:
#include <Array.au3>
Local $string = FileRead("example.yff")
Local $array = yffParse($string)
_ArrayDisplay($array)
; $string -> Der zu parsende String
; $del -> Array im folgenden Format:
; > [0] = Anzahl der Delimeter Zeichen
; > [1] = Asc(Delimeter 1)
; > [2] = Asc(Delimeter 2)
; > [x] = Asc(Delimeter x)
; $esc -> Array im folgenden Format:
; > [0] = Anzahl der Escape Zeichen
; > [1] = Asc(Escape 1)
; > [2] = Asc(Escape 2)
; > [x] = Asc(Escape x)
Func ycfParse($string, $del = Default, $esc = Default)
If $del = Default Then
Dim $del[3]
$del[0] = 2
$del[1] = Asc("<")
$del[2] = Asc(">")
EndIf
If $esc = Default Then
Dim $esc[2]
$esc[0] = 1
$esc[1] = Asc("\")
EndIf
Local $str = StringToASCIIArray($string, 0, StringLen($string), 1)
Local $dat[2] = [1]
Local $mod, $flg
For $i = 0 To UBound($str) - 1
Switch $mod
; searching for delimeter or escape
Case 0
; check for delimeter
If yffCheck($str[$i], $del) Then $mod = 1
; check for escape
If yffCheck($str[$i], $esc) Then $mod = 2
; getting the data
Case 1
; check for delimeter
If Not $flg And yffCheck($str[$i], $del) Then
$mod = 0
; check for array size
$dat[0] += 1
If $dat[0] = UBound($dat) Then ReDim $dat[($dat[0]) * 2]
Else
; check for escape
If Not $flg And yffCheck($str[$i], $esc) Then
$flg = True
Else
$dat[$dat[0]] &= Chr($str[$i])
$flg = False
EndIf
EndIf
; skip one
Case 2
$mod = 0
$flg = False
EndSwitch
Next
; crop array
ReDim $dat[$dat[0]]
$dat[0] -= 1
Return $dat
EndFunc
Func yffCheck($char, $sym)
For $i = 1 To $sym[0]
If $char = $sym[$i] Then Return True
Next
EndFunc
Alles anzeigen
Der Parser selber schmeißt euch einfach eine Liste mit allen Datensätzen zu. Im Index 0 ist immer die Anzahl der Datensätze im Array. Wie Ihr die Datensätze interpretiert bleibt euch überlassen. Hier mal ein einfaches Beispiel um Daten im Key = Value Format zu speichern:
--------------------------------------------------------------------------------
Die "=" Zeichen im Dokument dienen nur für die Lesbarkeit des Menschen. Sie
haben für den Parser absolut keine Bedeutung da diese außerhalb der Delimeter
liegen. Sie werden also schlichtweg ignoriert...
--------------------------------------------------------------------------------
<Key 1> = <Value 1>
<Key 2> = <Value 2>
<Key 3> Kommentar zu "Key 3"
<Value 3> Kommentar zu "Value 3"
--------------------------------------------------------------------------------
Alles anzeigen
Und hier das Skript:
#include <Array.au3>
Local $string = FileRead("example.yff")
Local $array = yffParse($string)
Local $keyvalue[$array[0] / 2][2]
; Map 1D Array to 2D Array | Key=Value Format
For $i = 1 To $array[0] / 2
$keyvalue[$i - 1][0] = $array[$i * 2 - 1]
$keyvalue[$i - 1][1] = $array[$i * 2]
Next
_ArrayDisplay($keyvalue)
Func yffParse($string, $del = Default, $esc = Default)
If $del = Default Then
Dim $del[3]
$del[0] = 2
$del[1] = Asc("<")
$del[2] = Asc(">")
EndIf
If $esc = Default Then
Dim $esc[2]
$esc[0] = 1
$esc[1] = Asc("\")
EndIf
Local $str = StringToASCIIArray($string, 0, StringLen($string), 1)
Local $dat[2] = [1]
Local $mod, $flg
For $i = 0 To UBound($str) - 1
Switch $mod
; searching for delimeter or escape
Case 0
; check for delimeter
If yffCheck($str[$i], $del) Then $mod = 1
; check for escape
If yffCheck($str[$i], $esc) Then $mod = 2
; getting the data
Case 1
; check for delimeter
If Not $flg And yffCheck($str[$i], $del) Then
$mod = 0
; check for array size
$dat[0] += 1
If $dat[0] = UBound($dat) Then ReDim $dat[($dat[0]) * 2]
Else
; check for escape
If Not $flg And yffCheck($str[$i], $esc) Then
$flg = True
Else
$dat[$dat[0]] &= Chr($str[$i])
$flg = False
EndIf
EndIf
; skip one
Case 2
$mod = 0
$flg = False
EndSwitch
Next
; crop array
ReDim $dat[$dat[0]]
$dat[0] -= 1
Return $dat
EndFunc
Func yffCheck($char, $sym)
For $i = 1 To $sym[0]
If $char = $sym[$i] Then Return True
Next
EndFunc
Alles anzeigen
Nun denn, für meine Zwecke jedenfalls nutzbar. Ich dachte ich teile das mit euch. Wer einen besseren Parser schreiben möchte, nur zu. Ich hantiere mit eine kleine Menge an Daten dh. reicht mir die Geschwindigkeit aus. Jedoch würde ich mich natürlich über ein free update freuen.
Hauptsächtlich geht es bei dem Post darum eine einfache Möglichkeit zu haben Datensätze in menschenlesbarer Form zu speichern und abzurufen. Dabei sollte das Format nicht kompliziert sein aber funktionsreich genug um auch Datensätze zu erzeugen die via Software dann richtig aufbereitet werden können. Eine einfache Array Liste reicht da meiner Ansicht nach aus. Darauf lassen sich weitere Filter und Bedingungen anknüpfen um das Dateiformat zu extenden. Sprich: Datensätze weiter auswerten und entsprechend so zu kategorisieren wie gewünscht.