Difference between revisions of "Zeno file format"

Jump to navigation Jump to search
351 bytes removed ,  10:22, 22 February 2009
no edit summary
Line 1: Line 1:
ZenoReader/Library
= ZenoReader/Library =
Aus DirectWiki
Wechseln zu: Navigation, Suche
Die Dokumentation wurde für das Zeno-2.0-Format aktualisiert!  
Die Dokumentation wurde für das Zeno-2.0-Format aktualisiert!  
Inhaltsverzeichnis
 
1 Aufbau einer .zeno-Datei  
== Aufbau einer .zeno-Datei ==
1.1 Header
=== Header ===
1.2 Inhaltsverzeichnis
1.3 Zusatzdaten bei Indexdateien
2 NamespaceCounts-Tabelle
3 Integer-Komprimierung
  [bearbeiten]  Aufbau einer .zeno-Datei
[bearbeiten]  Header  
Nach dem Öffnen der Datei muss der Header eingelesen werden, er steht am Anfang der Datei.  
Nach dem Öffnen der Datei muss der Header eingelesen werden, er steht am Anfang der Datei.  


Line 36: Line 28:
  end;
  end;


[bearbeiten]  Inhaltsverzeichnis  
=== Inhaltsverzeichnis ===
Um den Code erstmal einfach zu halten, wird das ganze Inhaltsverzeichnis auf einen Schlag eingelesen (kann mit dem Parameter /f erzwungen werden, bringt Geschwindigkeitsgewinn auf Kosten von RAM).  
Um den Code erstmal einfach zu halten, wird das ganze Inhaltsverzeichnis auf einen Schlag eingelesen (kann mit dem Parameter /f erzwungen werden, bringt Geschwindigkeitsgewinn auf Kosten von RAM).  
  TWPVLibrary = class (TObject)
  TWPVLibrary = class (TObject)
Line 66: Line 58:


Es ist zu beachten, dass der Record auf Bytegrenzen gepackt ist, d.h. ohne rExtra genau 26 Byte groß ist. Durch das zwingene Nullbyte in rExtra auch bei einem leeren Artikeltitel ist somit jeder Eintrag mindestens 27 Byte groß.  
Es ist zu beachten, dass der Record auf Bytegrenzen gepackt ist, d.h. ohne rExtra genau 26 Byte groß ist. Durch das zwingene Nullbyte in rExtra auch bei einem leeren Artikeltitel ist somit jeder Eintrag mindestens 27 Byte groß.  
[bearbeiten]  Zusatzdaten bei Indexdateien  
 
=== Zusatzdaten bei Indexdateien ===
Die Artikel der Indexdateien gehören zu folgenden Namespaces:  
Die Artikel der Indexdateien gehören zu folgenden Namespaces:  
V/xxx: alle Artikel der Kategorie xxx, alphabetisch sortiert
* V/xxx: alle Artikel der Kategorie xxx, alphabetisch sortiert
W/xxx: alle Artikel der Kategorie xxx, nach Artikelindex sortiert
* W/xxx: alle Artikel der Kategorie xxx, nach Artikelindex sortiert
X/xxx: alle Artikel in denen das Wort xxx vorkommt, nach Artikelindex sortiert
* X/xxx: alle Artikel in denen das Wort xxx vorkommt, nach Artikelindex sortiert
Y/xxx: reserviert für Weblinks (in WP-zeno nicht vorhanden)
* Y/xxx: reserviert für Weblinks (in WP-zeno nicht vorhanden)
Z/xxx: reserviert für hervorgehobene Wortgruppen (in WP-zeno nicht vorhanden)
* Z/xxx: reserviert für hervorgehobene Wortgruppen (in WP-zeno nicht vorhanden)


Ein Eintrag für V und W ist ein 4-Byte-Integer (Artikelindex), ein Eintrag für X ein 8-Byte-Integer (Artikelindex und Wortindex).  
Ein Eintrag für V und W ist ein 4-Byte-Integer (Artikelindex), ein Eintrag für X ein 8-Byte-Integer (Artikelindex und Wortindex).  
Line 82: Line 75:


Für jede Gewichtung folgt ein Eintrag:  
Für jede Gewichtung folgt ein Eintrag:  
len: Länge des Streams in der Datei in Bytes
* len: Länge des Streams in der Datei in Bytes
firstArticleIndex: bei V, W, X
* firstArticleIndex: bei V, W, X
firstWordIndex: nur bei X
* firstWordIndex: nur bei X


Der erste Eintrag einer jeden Gewichtung befindet sich somit in RZenoArticle und ist schnell zu lesen. Gibt es nur diesen einen Eintrag, ist len=0, ansonsten gibt es weitere Einträge in der Indexdatei.  
Der erste Eintrag einer jeden Gewichtung befindet sich somit in RZenoArticle und ist schnell zu lesen. Gibt es nur diesen einen Eintrag, ist len=0, ansonsten gibt es weitere Einträge in der Indexdatei.  
[bearbeiten]  NamespaceCounts-Tabelle  
 
=== NamespaceCounts-Tabelle ===
Gültige Namespace-Characters (TLibNamespaceChar) gehen von '-' bis 'Z', also 46 verschiedene Namespaces. Da die Artikel aufsteigend nach Namespace angelegt sind, kann man mit dem Startindex und der Anzahl der Artikel für diesen Namespace gut auf einen bestimmten Namespace zugreifen, z.B. wenn die Wildcardsuche über den ganzen Namespace 'X' (und nur über den) laufen soll.  
Gültige Namespace-Characters (TLibNamespaceChar) gehen von '-' bis 'Z', also 46 verschiedene Namespaces. Da die Artikel aufsteigend nach Namespace angelegt sind, kann man mit dem Startindex und der Anzahl der Artikel für diesen Namespace gut auf einen bestimmten Namespace zugreifen, z.B. wenn die Wildcardsuche über den ganzen Namespace 'X' (und nur über den) laufen soll.  
  RNamespaceStartCount = record
  RNamespaceStartCount = record
Line 96: Line 90:
  RNamespaceStartCountArray = array [TLibNamespaceChar] of RNamespaceStartCount;
  RNamespaceStartCountArray = array [TLibNamespaceChar] of RNamespaceStartCount;


[bearbeiten]  Integer-Komprimierung  
=== Integer-Komprimierung ===
Bei komprimierten Indexdateien sind sowohl die Zusatzdaten (s.o.) als auch die Daten in der Indexdatei wie folgt komprimiert:  
Bei komprimierten Indexdateien sind sowohl die Zusatzdaten (s.o.) als auch die Daten in der Indexdatei wie folgt komprimiert:  
Die ersten beiden Bits des ersten Bytes sind reserviert und codieren ob es sich insgesamt um 1, 2, 3 oder 4 Byte handelt, die einen (Pseudo-)-Integer zwischen 0 und $4040403F codieren.  
Die ersten beiden Bits des ersten Bytes sind reserviert und codieren ob es sich insgesamt um 1, 2, 3 oder 4 Byte handelt, die einen (Pseudo-)-Integer zwischen 0 und $4040403F codieren.  
1 Byte : Wertebereich 0 bis 26-1
* 1 Byte : Wertebereich 0 bis 26-1
2 Bytes: Wertebereich 26 bis 214+26-1
* 2 Bytes: Wertebereich 26 bis 214+26-1
3 Bytes: Wertebereich 214+26 bis 222+214+26-1
* 3 Bytes: Wertebereich 214+26 bis 222+214+26-1
3 Bytes: Wertebereich 222+214+26 bis 230+222+214+26-1
* 3 Bytes: Wertebereich 222+214+26 bis 230+222+214+26-1


Ferner wird bei manchen Streams von Interegers noch die Eigenschaft ausgenutzt, dass sie aufsteigend sortiert sind, was bei der Indexdatei für die Namespaces "W" und "X" gilt. Der Integer-Compressor/Decompressor hat eine Property, die das Verhalten steuert:  
Ferner wird bei manchen Streams von Interegers noch die Eigenschaft ausgenutzt, dass sie aufsteigend sortiert sind, was bei der Indexdatei für die Namespaces "W" und "X" gilt. Der Integer-Compressor/Decompressor hat eine Property, die das Verhalten steuert:  
Line 123: Line 117:
(3,27), (2,3), (0,17), (7, 25)
(3,27), (2,3), (0,17), (7, 25)


 
= ZenoReader/Qunicode =
 
== Codierung ==
=ZenoReader/Qunicode=
Aus DirectWiki
Wechseln zu: Navigation, Suche
Inhaltsverzeichnis
1 Codierung
2 Vergleichsroutine
3 Folding
4 Testroutine
  [bearbeiten]  Codierung  
Die Artikeltitel sind in einem eigenen Format codiert, da UTF8 oder ähnliches Geschwindigkeitsnachteile beim Sortieren und Matchen bringt. Es ist eine Ad-hoc-Codierung, die davon ausgeht, dass Characters mit den Werten 1 und 2 nicht im Titel vorkommen. Ein Byte mit dem Wert 1 zeigt an, dass die nachfolgenden 2 Bytes als 2-Byte-Unicode zu interpretieren sind. Um Nullbytes im String zu vermeiden wird der Wert 2 genommen, wenn das niedrige Byte des 2-Byte-Unicode null ist, das dann als 1 (?) codiert ist. Das hohe Byte kann niemals Null sein, da $20..$FF sowieso als ein Byte codiert werden.  
Die Artikeltitel sind in einem eigenen Format codiert, da UTF8 oder ähnliches Geschwindigkeitsnachteile beim Sortieren und Matchen bringt. Es ist eine Ad-hoc-Codierung, die davon ausgeht, dass Characters mit den Werten 1 und 2 nicht im Titel vorkommen. Ein Byte mit dem Wert 1 zeigt an, dass die nachfolgenden 2 Bytes als 2-Byte-Unicode zu interpretieren sind. Um Nullbytes im String zu vermeiden wird der Wert 2 genommen, wenn das niedrige Byte des 2-Byte-Unicode null ist, das dann als 1 (?) codiert ist. Das hohe Byte kann niemals Null sein, da $20..$FF sowieso als ein Byte codiert werden.  
Beispielhaft sei die Funktion QunicodeToXML angeführt:  
Beispielhaft sei die Funktion QunicodeToXML angeführt:  
<pre>
function QunicodeToXML (const qunicode: string): string;
function QunicodeToXML (const qunicode: string): string;
type
type
Line 175: Line 161:
   SetLength(Result, l);
   SetLength(Result, l);
end;
end;
</pre>


Diese Codierung erlaubt ein schnelleres Sortieren und Matchen und ist - bei westeuropäischen Texten - meist sogar kürzer als UTF-8, was daran liegt, dass Umlaute weiterhin nur ein Byte benötigen.  
Diese Codierung erlaubt ein schnelleres Sortieren und Matchen und ist - bei westeuropäischen Texten - meist sogar kürzer als UTF-8, was daran liegt, dass Umlaute weiterhin nur ein Byte benötigen.  
[bearbeiten]  Vergleichsroutine  
 
== Vergleichsroutine ==
Für das Sortieren und Finden benötigt man eine Vergleichsroutine zweier Qunicode-Strings:  
Für das Sortieren und Finden benötigt man eine Vergleichsroutine zweier Qunicode-Strings:  
<pre>
function PCharCompareUseTable (p1, p2: PChar): integer;
function PCharCompareUseTable (p1, p2: PChar): integer;
var
var
Line 228: Line 218:
  end;
  end;
end;
end;
</pre>


Entscheidend ist dabei das Folding von 2-Byte-Zeichen auf 1-Byte-Zeichen, um Umlaute und diakritische Zeichen "richtiger" zu sortieren als es per Bytevergleich wäre. "Richtiger" deshalb, da "richtig" einen enormen Aufwand darstellen würde, siehe Unicode Collation Algorithm.  
Entscheidend ist dabei das Folding von 2-Byte-Zeichen auf 1-Byte-Zeichen, um Umlaute und diakritische Zeichen "richtiger" zu sortieren als es per Bytevergleich wäre. "Richtiger" deshalb, da "richtig" einen enormen Aufwand darstellen würde, siehe Unicode Collation Algorithm.  
PCharCompareUseTable  besteht aus zwei Teilen: Zuerst wird der String gefaltet verglichen, wenn er gleich ist, wird der Unicode-Wert genommen. Dies hat folgende Vorteile:  
PCharCompareUseTable  besteht aus zwei Teilen: Zuerst wird der String gefaltet verglichen, wenn er gleich ist, wird der Unicode-Wert genommen. Dies hat folgende Vorteile:  
A, a, Ä, a, Ā, ā usw. stehen vor B, b, ...  
* A, a, Ä, a, Ā, ā usw. stehen vor B, b, ...  
A, a, Ä, a, Ā, ā sortiert ergibt genau die Reihenfolge, die hier steht  
* A, a, Ä, a, Ā, ā sortiert ergibt genau die Reihenfolge, die hier steht  
 
Wichtig: Diese Sortierung entspricht keiner Norm - sie ist eher so etwas wie eine intuitive Sotierung, die für Sprachen im Latin-Alphabet brauchbare Ergebnisse liefert. Da Offline-Wikipedia in allen Sprachen funktionieren soll, wäre es wünschenswert, doch den Unicode Collation Algorithm zu implementieren - es muss erstmal eine Aufwands- und Runtimeverhalten-Abschätzung her.  
Wichtig: Diese Sortierung entspricht keiner Norm - sie ist eher so etwas wie eine intuitive Sotierung, die für Sprachen im Latin-Alphabet brauchbare Ergebnisse liefert. Da Offline-Wikipedia in allen Sprachen funktionieren soll, wäre es wünschenswert, doch den Unicode Collation Algorithm zu implementieren - es muss erstmal eine Aufwands- und Runtimeverhalten-Abschätzung her.  
[bearbeiten]  Folding  
 
== Folding ==
_ZZZ ist ein array [0..$FFFF], in dem jedem 2-Byte-Unicode ein ASCII-Zeichen 0..$7F zugeordnet wird. Die Tabelle: qunicode.zip.  
_ZZZ ist ein array [0..$FFFF], in dem jedem 2-Byte-Unicode ein ASCII-Zeichen 0..$7F zugeordnet wird. Die Tabelle: qunicode.zip.  
Diese Tabelle kann für alle 2-Byte-Unicodes erweitert werden; das Folding von z.B. chinesischen Zeichen auf ASCII-Zeichen stellt dann eine Art Hashfunktion dar, mit der bei Eingabe eines chinesischen Strings ein passender Match (aber u.U. noch mehr) gefunden werden kann. Die Tabelle sollte Bestandteil einer Zeno-Datei sein!  
Diese Tabelle kann für alle 2-Byte-Unicodes erweitert werden; das Folding von z.B. chinesischen Zeichen auf ASCII-Zeichen stellt dann eine Art Hashfunktion dar, mit der bei Eingabe eines chinesischen Strings ein passender Match (aber u.U. noch mehr) gefunden werden kann. Die Tabelle sollte Bestandteil einer Zeno-Datei sein!  
[bearbeiten]  Testroutine  
 
== Testroutine ==
Da die Artikel in einer Zeno-Datei vorsortiert sind, ist es ein guter Test festzustellen, ob mit der selbst geschriebenen Compare-Routine dies auch weiterhin stimmt. Das muss zwingend der Fall sein, damit beim Laden des Headers der Zeno-Datei nicht jedesmal sortiert werden muss.
Da die Artikel in einer Zeno-Datei vorsortiert sind, ist es ein guter Test festzustellen, ob mit der selbst geschriebenen Compare-Routine dies auch weiterhin stimmt. Das muss zwingend der Fall sein, damit beim Laden des Headers der Zeno-Datei nicht jedesmal sortiert werden muss.

Navigation menu