Vorbemerkungen Übungen Textalgorithmen Peter Becker FH Bonn-Rhein-Sieg Fachbereich Angewandte Informatik [email protected] • In die Vorlesung integriert • Bearbeitungszeit: abhängig von den Aufgaben, i.d.R. eine oder zwei Wochen Vorlesung Sommersemester 2002 • Theorie- und Programmieraufgaben • Programmieraufgaben können in einer beliebigen Programmiersprache gelöst werden, wenn nicht explizit eine Sprache (Java, C) vorgeben wird. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Vorbemerkungen Allgemeines zur Vorlesung 2 Vorbemerkungen TB, LN, Prüfung • Es gibt eine Homepage zur Vorlesung: http://www2.inf.fh-rhein-sieg.de/˜pbecke2m/textalgorithmen/ • Die Vorlesung wird folienbasiert gehalten. • TB: Anwesenheit, ein paar Übungsaufgaben demonstrieren • Die Folien zur Vorlesung (Skript) stehen auf der Homepage vor der Vorlesung zur Verfügung. • LN: Übungsaufgaben ++, (Richtlinie 50% der zu vergebenden Punkte) • Format: PDF, zwei- und vierseitig • Sie können also die ausgedruckten Folien mit in die Vorlesung bringen und dort mit schriftlichen Bemerkungen versehen. • Benutzen Sie zum Drucken bitte die vierseitige Version des Skriptes. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3 Vorbemerkungen 1. Suche von Mustern Einf ¨uhrung Literatur 1 Suche von Mustern M. Crochemore, W. Rytter, Text Algorithms, Oxford University Press, 1994. Die Suche von einem Muster in einem Text wird auch als String Matching oder Pattern Matching bezeichnet. D. Gusfield, Algorithms on Strings, Trees, and Sequences, Cambride University Press, 1997. Generell besteht die Aufgabe darin, • einen String (das Muster, Pattern) der Länge m L. Schmitz, Syntaxbasierte Programmierwerkzeuge, Teubner, 1995. • in einem Text der Länge n zu finden, wobei n > m gilt. Je nach Freiheitsgraden bei der Suche unterscheidet man verschiedene Arten von String-Matching-Problemen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4 Vorbemerkungen Inhalt 1. Suche von Mustern 6 Einf ¨uhrung Arten von String-Matching-Problemen 1. Suche von Mustern Exaktes String-Matching: Wo tritt ein String pat in einem Text text auf? Beispiel: fgrep 2. Suffix-Bäume Matching von Wortmengen: Gegeben sei eine Menge S von Strings. Wo tritt in einem Text ein String aus S auf? Beispiel: agrep 3. reguläre Ausdrücke, approximative Mustersuche 4. lexikalische Analyse und Parsing Matching regulärer Ausdr ücke: Welche Stellen in einem Text passen auf einen regulären Ausdruck? Beispiel: grep, egrep 5. XML und Analyse von XML-Dokumenten Approximatives String-Matching: Welche Stellen in einem Text passen am besten auf ein Muster (Best-Match-Anfrage)? 6. Textkompression Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 7 1. Suche von Mustern Einf ¨uhrung Welche Stellen in einem Text stimmen mit einem Muster bis auf d Fehler überein (Distance-Match-Anfrage)? Beispiel: agrep Editierdistanz: Wie kann man am “günstigsten” einen String s in einen String t überführen? Beispiel: diff Wo braucht man String-Matching-Verfahren? Z.B.: 1. Suche von Mustern Einf ¨uhrung • s[i] bezeichnet das i-te Element eines Strings s (1 ≤ i ≤ |s|). s[i . . . j] bezeichnet den String s[i]s[i + 1] . . . s[j]. Für i > j gelte s[i . . . j] = . • Für einen String s (mit m = |s|) bezeichnet sR die Umkehrung s[m]s[m − 1] · · · s[1] von s. • Für zwei String x und y gilt x = y genau dann, wenn |x| = |y| = m und x[i] = y[i] für alle 1 ≤ i ≤ m gilt. • Volltextdatenbanken, Retrievalsysteme, Suchmaschinen • Bioinformatik • Wenn ω = xyz ein String ist, dann ist x ein Präfix und z ein Suffix von ω. ☞ In diesem Kapitel lernen wir effziente Algorithmen für exaktes StringMatching kennen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1. Suche von Mustern 8 Einf ¨uhrung Gilt ω 6= x (ω 6= z), dann ist x (z) ein echter Präfix (echter Suffix) von ω. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1. Suche von Mustern 10 Einf ¨uhrung Bezeichnungen • Ein String x (mit m = |x|) heißt Substring (Faktor) von y, wenn ein i existiert mit x = y[i . . . i + m − 1]. Andere Sprechweisen: x tritt in y an Position i auf bzw. Position i ist ein Match für x in y. • Ein Alphabet Σ ist eine endliche Menge von Symbolen. |Σ| bezeichnet die Kardinalität von Σ. • x (mit m = |x|) heißt Subsequenz von y wenn Positionen i1 < i2 · · · < im existieren mit x = y[i1]y[i2] . . . y[im]. • Ein String (Zeichenkette, Wort) s über einem Alphabet Σ ist eine endliche Folge von Symbolen aus Σ. |s| bezeichnet die Länge von s. • bezeichnet den leeren String. • Wenn x und y Strings sind, dann bezeichnet xy die Konkatenation von x und y. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 9 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 11 1. Suche von Mustern Einf ¨uhrung 1. Suche von Mustern Der naive Algorithmus • Zunächst wird die Variante “von links nach rechts” betrachtet. Exaktes String-Matching Problem 1.1. [Exaktes String-Matching] Gegeben sind die Strings pat und text. (a) Man bestimme, ob pat ein Substring von text ist. (b) Man bestimme die Menge aller Positionen, an denen pat in text auftritt. Diese Menge wird mit M AT CH(pat, text) bezeichnet. • Im folgenden wird nur die Variante (a) von Problem 1.1 betrachtet. • Algorithmen für die Variante (b) erhält man durch einfache Modifikationen der Algorithmen für (a). Algorithmus 1.1. [Naives String-Matching von links nach rechts] i := 1 while i ≤ n − m + 1 do j := 1 while j ≤ m and pat[j] = text[i + j − 1] do j := j + 1 end if j = m + 1 then return true i := i + 1 end return false Satz 1.1. Der naive Algorithmus 1.1 löst Problem 1.1 in Zeit O(nm) und Platz O(m). • Im folgenden sei m = |pat| und n = |text|. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1. Suche von Mustern 12 Der naive Algorithmus Der naive Algorithmus 1. Suche von Mustern 14 Der naive Algorithmus Analyse für naives String-Matching Der naive Ansatz besteht darin, für jede Position von text (bzw. solange pat ab der aktuellen Position in text paßt) von neuem zu testen, ob pat an dieser Position auftritt. • Für pat = am−1b und text = an benötigt Algorithmus 1.1 (n − m + 1)m = nm − m2 + m Das allgemeine Schema für solch einen naiven Algorithmus lautet: for i := 1 to n − m + 1 do man prüfe, ob pat = text[i . . . i + m − 1] gilt Zeichenvergleiche (Worst Case). • Die Prüfung kann nun “von links nach rechts” oder “von rechts nach links” erfolgen. • Dies führt zu unterschiedlichen naiven Algorithmen und darauf aufbauend zu unterschiedlichen Ansätzen der Verbesserung. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 13 • Bei einem binären Alphabet und zufällig erzeugten pat und text (jedes Zeichen unabhängig und jedes Symbol mit Wahrscheinlichkeit 1/2) ergibt sich für die durchschnittliche Anzahl an Zeichenvergleichen: (2 − 2−m)n + O(1) Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 15 1. Suche von Mustern Der naive Algorithmus • Trotz der im Durchschnitt linearen Laufzeit lohnt sich der Einsatz von “besseren” String-Matching-Algorithmen, denn: 1. Suche von Mustern Morris und Pratt Veranschaulichung: pat: – die nachfolgenden String-Matching-Algorithmen haben sich nicht nur in der Theorie, sondern auch in der Praxis als erheblich effizienter erwiesen, und – die Realität gehorcht nicht immer den Gesetzen der Wahrscheinlichkeitstheorie. a b c a - - - - - pat: a b c a b c d - - - - - text: - - - - - a b c a b c a - - - - ↑ ↑ ↑ i i+s i+j−1 s ist hier der Betrag, um den pat nach rechts verschoben wird. Nach einem Mismatch an Position j von pat kann nur dann an i + s ein Match vorliegen, wenn pat[1 . . . j − s − 1] ein Suffix von pat[1 . . . j − 1] ist. Mit k := j − s folgt: pat[1 . . . k − 1] ist Suffix von pat[1 . . . j − 1]. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1. Suche von Mustern 16 Morris und Pratt Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 18 1. Suche von Mustern Morris und Pratt Veranschaulichung: String-Matching nach Morris und Pratt naechstmoeglicher Match s Algorithmus 1.1 ist naiv im folgenden Sinn: nach jedem Mismatch an Stelle j von pat wird vergessen, daß pat bis zur Position j − 1 auf text paßt. Mismatch Pattern 1 Kommt es an Stelle j von pat zu einem Mismatch, so gilt: pat[1 . . . j − 1] = text[i . . . i + j − 2] j Text Dies kann wie folgt ausgenutzt werden: Angenommen, pat tritt in text an einer Position i + s mit i < i + s < i + j − 1 auf. Dann muß gelten: pat[1 . . . j − s − 1] = pat[1 + s . . . j − 1] Damit man keinen Match verpaßt, muß s möglichst klein und somit k möglichst groß gewählt werden. Konsequenzen für einen verbesserten Algorithmus: Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 17 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 19 1. Suche von Mustern Morris und Pratt • Man ermittle in einer Preprocessingphase zu jedem 1 ≤ j ≤ m das größte k, so daß pat[1 . . . k − 1] echter Suffix von pat[1 . . . j − 1] ist. Der entsprechende Betrag wird mit border[j] bezeichnet. 1. Suche von Mustern Morris und Pratt Lemma 1.2. Wenn border[j] (für 1 ≤ j ≤ m) bereits vorliegt, löst Algorithmus 1.2 Problem 1.1 in Zeit O(n). Beweis. border[j] := max {k | pat[1 . . . k − 1] = pat[j − k + 1 . . . j − 1]} • Es gibt höchstens n − m + 1 nicht erfolgreiche Vergleiche (nämlich höchstens einer für jedes i). 1≤k≤j−1 bzw. • Man betrachte nun den Term i + j. Es gilt 2 ≤ i + j ≤ n + 1. border[j] := max {k | pat[1 . . . k−1] ist echter Suffix von pat[1 . . . j−1]} 1≤k≤j−1 • Immer wenn der Vergleich pat[j] = text[i + j − 1] erfolgreich war, wird i + j um eins erhöht. Weiterhin gelte border[1] = 0 • Insgesamt wird i + j in (A) und (B) nicht verringert. • Im Algorithmus schiebe man bei einem Mismatch an Position j des Pattern dieses um s = j − border[j] Stellen nach rechts. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 20 1. Suche von Mustern Morris und Pratt • Durch die Maximalität ist s ein “safe shift”. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1. Suche von Mustern 22 Morris und Pratt ⇒ und somit O(n) Vergleiche insgesamt. Algorithmus 1.2. [Morris und Pratt] i := 1; j := 1 while i ≤ n − m + 1 do while j ≤ m and pat[j] = text[i + j − 1] do j := j + 1 end if j = m + 1 then return true i := i + j − border[j] j := max(border[j], 1) end return false ⇒ Es gibt ≤ n erfolgreiche Vergleiche 2 Es bleibt das Problem, border[j] berechnen zu müssen. Zunächst ein Beispiel für border[j]. (A) (B) 21 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 23 1. Suche von Mustern Morris und Pratt 1. Suche von Mustern Fibonacci-Strings Morris und Pratt Berechnung von border[j] Definition 1.1. Der n-te Fibonacci-String Fn (n ≥ 0) ist wie folgt definiert: • F0 = • Zur Berechnung der Tabelle border wird eine spezielle Version von Algorithmus 1.2 benutzt. • Man nutzt dabei die aus dem Beweis von Lemma 1.2 bekannte Tatsache aus, daß der Betrag i + j nie verringert wird. • F1 = b • F2 = a • Fn = Fn−1Fn−2 für n > 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 24 1. Suche von Mustern Morris und Pratt 1 a 0 2 b 1 3 a 1 4 a 2 5 b 2 6 a 3 7 b 4 8 a 3 9 a 4 10 b 5 11 a 6 12 a 7 13 b 5 1 a 0 2 b 1 3 a 1 4 b 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5 c 3 6 a 1 Morris und Pratt 7 a 2 8 b 2 9 a 3 10 c 4 11 c 1 12 b 1 13 a 1 border[1] := 0 for j := 2 to m do border[j] := 1 end i := 2 j := 1 while i ≤ m do while i + j − 1 ≤ m and pat[j] = pat[i + j − 1] do j := j + 1 border[i + j − 1] := j end i := i + j − border[j] j := max(border[j], 1) end Für ababcaabaccba ergibt sich: j pat[j] border[j] 1. Suche von Mustern 26 Algorithmus 1.3. Beispiel 1.1. border[j] lautet für F7: j pat[j] border[j] Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 25 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 27 1. Suche von Mustern Morris und Pratt Lemma 1.3. Algorithmus 1.3 benötigt zur Berechnung der Tabelle border höchstens O(m) Vergleiche. Beweis. Analog zu Lemma 1.2. 2 1. Suche von Mustern Knuth, Morris und Pratt Es kommt also an der gleichen Stelle des Textes direkt wieder zu einem Mismatch. Dies war abzusehen, denn für pat gilt: pat[6] = pat[border[6]] Veranschaulichung: zwangslaeufiger Mismatch s Mismatch != notwendig Pattern 1 j Text Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 28 1. Suche von Mustern Knuth, Morris und Pratt Der Algorithmus von Morris und Pratt kann noch effizienter gemacht werden. Man betrachte folgendes Beispiel: a b a a b a - - 1. Suche von Mustern Knuth, Morris und Pratt pat[k] 6= pat[j] verschärft werden. Konsequenzen für eine Verbesserung: man benutze statt border[j] die folgende Variante sborder[j]: text: - - - - - a b a a b c - Es kommt für j = 6 zu einem Mismatch. Wegen border[6] = 3 wird pat um drei Zeichen nach rechts geschoben. pat: 30 Offensichtlich kann die bisher genutzte Bedingung, daß pat[1 . . . k − 1] Suffix von pat[1 . . . j − 1] ist, um die Bedingung Knuth, Morris und Pratt pat: Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 sborder[j] := max {k | pat[1 . . . k−1] = pat[j−k+1 . . . j−1] und pat[k] 6= pat[j]} 1≤k≤j−1 Falls kein solches k existiert, gelte sborder[j] = 0 a b a a b a - - text: - - - - - a b a a b c - Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 29 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 31 1. Suche von Mustern Knuth, Morris und Pratt 1 a 0 0 2 b 1 1 3 a 0 1 4 a 2 2 5 b 1 2 6 a 0 3 7 b 4 4 8 a 0 3 9 a 2 4 10 b 1 5 11 a 0 6 12 a 7 7 Knuth, Morris und Pratt Satz 1.4. Die Algorithmen 1.3 und 1.4 lösen Problem 1.1 in Zeit O(n + m) und Platz O(m). Beispiel 1.2. sborder[j] lautet für F7 (vgl. Beispiel 1.1): j pat[j] sborder[j] border[j] 1. Suche von Mustern 13 b 1 5 Beweis. sborder kann wie border in Zeit O(m) berechnet werden. Durch die Verwendung von sborder statt border finden in Algorithmus 1.4 nicht mehr Vergleiche statt als in Algorithmus 1.2. 2 Algorithmus 1.4. [Knuth, Morris und Pratt] Der Algorithmus von Knuth, Morris und Pratt ist identisch zu Algorithmus 1.2 bis auf die Tatsache, daß statt border die strengere Variante sborder verwendet wird. Zur Berechnung von sborder sind ebenfalls nur marginale Änderungen gegenüber Algorithmus 1.3 notwendig. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1. Suche von Mustern 32 Knuth, Morris und Pratt Algorithmus 1.5. [Berechnung von sborder] 1. Suche von Mustern 34 Knuth, Morris und Pratt Beispiel 1.3. [Algorithmus von Knuth, Morris und Pratt] Der String F7 wird in dem String for j := 1 to m do sborder[j] := 0 end i := 2 j := 1 while i ≤ m do while i + j − 1 ≤ m and pat[j] = pat[i + j − 1] do j := j + 1 sborder[i + j − 1] := sborder[j] end sborder[i + j − 1] := max(j, sborder[i + j − 1]) i := i + j − sborder[j] j := max(sborder[j], 1) end Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 abaababaabacabaababaabaab gesucht. Nach dem Start des Algorithmus ergibt sich ein Mismatch für j = 12 und i = 1. j ↓ a b a a b a b a a b a a b a b a a b a b a a b a c a b a a b a b a a b a a b ↑ i 33 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 35 1. Suche von Mustern Knuth, Morris und Pratt Nun erhält man i := 1 + 12 − sborder[12] = 6 und j := sborder[12] = 7. j ↓ a b a a b a b a a b a a b Der Algorithmus von Boyer und Moore Algorithmus 1.6. [naives String-Matching von rechts nach links] i := 1 while i ≤ n − m + 1 do j := m while j ≥ 1 and pat[j] = text[i + j − 1] do j := j − 1 end if j = 0 then return true i := i + 1 end return false Es tritt wieder ein Mismatch auf, deshalb j := 4 und i := 9. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 36 Knuth, Morris und Pratt Die Mismatches setzen sich fort bis j = 1 und i = 13. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1. Suche von Mustern 38 Boyer und Moore Der Algorithmus von Boyer und Moore basiert auf der folgenden Überlegung: j ↓ a b a a b a b a a b a a b • Tritt in Algorithmus 1.6 an Stelle j ein Mismatch auf und kommt pat[j + 1 . . . m] nicht ein weiteres mal in pat als Substring vor, so kann pat gleich um m Zeichen nach rechts verschoben werden. a b a a b a b a a b a c a b a a b a b a a b a a b ↑ i • Vergleicht man dagegen von links nach rechts, kann pat nach einem Mismatch an Position j nie um mehr als j Positionen nach rechts verschoben werden (vgl. Algorithmus 1.2). Ab hier wird nun ein Match ermittelt. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Boyer und Moore Der Algorithmus von Boyer und Moore kann als eine verbesserte Variante eines naiven String-Matching-Algorithmus angesehen werden, bei dem pat mit text von rechts nach links verglichen wird. a b a a b a b a a b a c a b a a b a b a a b a a b ↑ i 1. Suche von Mustern 1. Suche von Mustern 37 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 39 1. Suche von Mustern Boyer und Moore Veranschaulichung: 1. Suche von Mustern Boyer und Moore Veranschaulichung: naechstmoeglicher Match Mismatch pat: Vergleich pat: - - - - a b c b c ↑ j text: - - - - - - a b c a b c - - - - ↑ ↑ i i+j−1 Pattern j 1 Text j−s ↓ - - - - a b c b c m ???? Damit man keinen Match verpaßt, muß s wiederum möglichst klein gewählt werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1. Suche von Mustern 40 Boyer und Moore Kommt es in Algorithmus 1.6 an der Stelle j von pat zu einem Mismatch, so gilt Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 42 1. Suche von Mustern Boyer und Moore Veranschaulichung: naechstmoeglicher Match • pat[j + 1 . . . m] = text[i + j . . . i + m − 1] und fuer BM2 != Vergleich • pat[j] 6= text[i + j − 1]. = fuer BM1 Mismatch Pattern Dies kann wie folgt ausgenutzt werden: Angenommen, pat tritt in text an einer Position i < i + s < i + m auf. Dann müssen die beiden folgenden Bedingungen gelten: j 1 Text m ???? (BM1) für alle j < k ≤ m : k ≤ s oder pat[k − s] = pat[k] (BM2) s < j =⇒ pat[j − s] 6= pat[j] Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 41 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 43 1. Suche von Mustern Boyer und Moore Die entsprechenden Werte werden in einer Preprocessingphase ermittelt und in der Shift-Tabelle D abgelegt. D[j] := min{s| (BM1) und (BM2) gilt für j und s } 1. Suche von Mustern Boyer und Moore Algorithmus 1.7 kann noch weiter verbessert werden: Tritt an Stelle m von pat (also bereits beim ersten Vergleich) ein Mismatch auf, so wird pat nur um eine Stelle nach rechts verschoben. Es sei s>0 last[c] := max {j | pat[j] = c} 1≤j≤m Der Algorithmus von Boyer und Moore verwendet nun im Falle eines Mismatches an Position j den in D[j] abgelegten Wert, um pat nach rechts zu verschieben. und last[c] := 0 falls c nicht in pat auftritt. last[c] gibt für ein c ∈ Σ die jeweils letzte Position von c in pat an. Kommt es nun an Stelle j zu einem Mismatch, kann statt i := i + D[j] die Anweisung i := i + max(D[j], j − last[text[i + j − 1]]) Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 44 1. Suche von Mustern Boyer und Moore Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 1. Suche von Mustern 46 Boyer und Moore Algorithmus 1.7. [Algorithmus von Boyer und Moore] verwendet werden. Damit ergeben sich noch größere Verschiebungen. i := 1 while i ≤ n − m + 1 do j := m while j ≥ 1 and pat[j] = text[i + j − 1] do j := j − 1 end if j = 0 then return true i := i + D[j] end return false Bemerkungen: 1 a 8 • Wird nur der Occurence-Shift verwendet, d.h. die Verschiebeanweisung lautet i := i + max(1, j − last[text[i + j − 1]]) D[j] lautet für F7: Beispiel 1.4. j pat[j] D[j] • Verschiebungen der Länge j −last[text[i+j −1]] heißen OccurrenceShift. 2 b 8 3 a 8 4 a 8 5 b 8 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 6 a 8 7 b 8 8 a 3 9 a 11 10 b 11 11 a 6 12 a 13 13 b 1 so spricht man von einem vereinfachten Boyer-Moore-Algorithmus. • Die Worst-Case-Laufzeit des vereinfachten Boyer-Moore-Algorithmus beträgt O(nm). 45 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 47 1. Suche von Mustern Boyer und Moore 1. Suche von Mustern • Auf gewöhnlichen Texten verhält sich die vereinfachte Version i.d.R. nur marginal schlechter als die ursprüngliche Version. Die Situation ist ähnlich wie beim Verfahren von Morris und Pratt. Statt eines Präfixes muß hier aber ein Suffix von pat mit einem inneren Teil von pat übereinstimmen. • Bei kleinem |Σ| ist die Occurence-Heuristik i.d.R. nutzlos. Vorgehensweise für die erste Phase: man berechnet (für 0 ≤ j ≤ m) rborder[j] mit naechstmoeglicher Match fuer Occurence rborder[j] := Mismatch Vergleich j Text max {k | pat[j + 1 . . . j + k − 1] = pat[m − k + 2 . . . m]} 1≤k≤m−j bzw. Pattern 1 Boyer und Moore rborder[j] := m max {k | pat[j+1 . . . j+k−1] ist echter Suffix von pat[j+1 . . . m]} 1≤k≤m−j Weiterhin gelte rborder[m] = 0. ???? Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 48 1. Suche von Mustern Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Boyer und Moore 50 1. Suche von Mustern Es bleibt das Problem, die Shift-Tabelle D zu berechnen. Dies geschieht in zwei Phasen. In der ersten Phase werden nur Verschiebungen der Form D[j] < j betrachtet. Für solche Verschiebungen muß gelten: Boyer und Moore Beispiel 1.5. rborder[j] lautet für F7: j pat[j] rborder[j] (BM1) pat[j + 1 . . . m] = pat[j + 1 − s . . . m − s] 0 6 1 a 5 2 b 4 3 a 3 4 a 2 5 b 6 6 a 5 7 b 4 8 a 3 9 a 2 10 b 1 11 a 1 12 a 1 13 b 0 Man beachte: (BM2) pat[j − s] 6= pat[j] • rborder[j] entspricht border[j] für patR. Veranschaulichung: pat: * pat: * * * * * * * b a b a a b b ↑ j−s a b a ↑ j a b a a b ← Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 a a b • Dementsprechend läßt sich rborder[j] analog zu Algorithmus 1.3 berechnen, wobei man nun aber von rechts nach links vorgeht. → s 49 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 51 1. Suche von Mustern Boyer und Moore 1. Suche von Mustern Boyer und Moore Beispiel 1.6. D[j] nach der ersten Phase für den String F7: i := m − 1 j := 1 while i >= 0 do while i − j + 1 >= 1 and pat[m − j + 1] = pat[i − j + 1] do j := j + 1 rborder[i − j + 1] = j end i := i − j + rborder[m − j + 1] j := max(rborder[m − j + 1], 1) end j pat[j] D[j] 1 a 13 2 b 13 3 a 13 4 a 13 5 b 13 6 a 13 7 b 13 8 a 3 9 a 13 10 b 13 11 a 6 12 a 13 13 b 1 Wegen (BM2) treten relevante Situationen zur Berechnung von D[j] nur im Falle eines Mismatch in der inneren While-Schleife auf. Dementsprechend wird zur Berechnung von D[j] der Algorithmus hinter der inneren While-Schleife um die folgenden Anweisungen erweitert: Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 52 1. Suche von Mustern Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Boyer und Moore 1. Suche von Mustern (BM1) pat[1 . . . t − 1] = pat[m − t + 2 . . . m] mit j ≤ m − t + 1 gelten. (BM2) ist in dieser Phase stets wahr und braucht nicht weiter betrachtet zu werden. Veranschaulichung: pat: * * * Boyer und Moore In der zweiten Phase werden Verschiebungen mit D[j] ≥ j betrachtet. Für solche Verschiebungen muß die Bedingung if j > 1 then s := m − i t := i − j + 1 if t + s > s then D[t + s] = min(s, D[t + s]) endif endif pat: * 54 Veranschaulichung: * * * * b a b ↑ t=i−j+1 b a b a a b a a ↑ t+s b ↑ i a a b ← a a pat: a b a a b a b a a b a a b → s pat: a b a a b a b a a b a a b ← s = m − t + 1 → ↑ ↑ ↑ j t−1 m−t+2 Damit steht der Algorithmus für die erste Phase. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 b 53 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 55 1. Suche von Mustern Boyer und Moore 1. Suche von Mustern Boyer und Moore Algorithmus 1.8. [Berechnung der Shift-Tabelle f ür Boyer und Moore] naechstmoeglicher Match Der größte mögliche Wert für t ergibt sich durch rborder[0] (Länge des längsten Substrings, der sowohl echter Präfix als auch echter Suffix ist). /* Initialisierung */ rborder[m] := 0; D[m] := 1 for j := m − 1 downto 0 do rborder[j] = 1; D[j] = m end /* Phase 1 */ i := m − 1 j := 1 while i >= 0 do while i − j + 1 >= 1 and pat[m − j + 1] = pat[i − j + 1] do j := j + 1 rborder[i − j + 1] = j end Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Vergleich = fuer BM1 Mismatch Pattern 1 Text t j m ???? Es werden nun in absteigender Reihenfolge mögliche Werte für t betrachtet, und D[j] wird entsprechend korrigiert. 1. Suche von Mustern 56 Boyer und Moore Die weiteren möglichen Werte für t ergeben sich durch rborder[m−t+1]. t := rborder[0] l := 1 while t > 0 do s=m−t+1 for j := l to s do D[j] := min(D[j], s) end t := rborder[s] l := s + 1 end Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 57 1. Suche von Mustern 58 Boyer und Moore if j > 1 then s := m − i t := i − j + 1 if t + s > s then D[t + s] = min(s, D[t + s]) endif endif i := i − j + rborder[m − j + 1] j := max(rborder[m − j + 1], 1) end /* Phase 2 */ t := rborder[0] l := 1 while t > 0 do s=m−t+1 for j := l to s do Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 59 1. Suche von Mustern Boyer und Moore 1. Suche von Mustern Boyer und Moore D[j] := min(D[j], s) end t := rborder[s] l := s + 1 end Bemerkungen: Beispiel 1.7. [Algorithmus von Boyer und Moore] Der String F7 wird in dem String abaababaabacabaababaabaab gesucht. • Würde man statt (BM1) und (BM2) nur (BM1) verwenden, so wäre keine lineare Laufzeit mehr gewährleistet (O(mn)). a a b a b a a b a b a a b a a b a b a a a b a a b • Als scharfe obere Schranke für die Anzahl an Zeichenvergleichen ergibt sich 3n. b a a b a a a b a a a a b a a b b b a b b a a b a a b a a b a a b a a b a b b a a a a b a a b a a b a b b • Sucht man mit dem Algorithmus von Boyer und Moore nach allen Matches für pat in text, so ist die Laufzeit ebenfalls O(mn). a b a a b a b a a b a c a b a a b a b a a b a a b Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 60 1. Suche von Mustern Boyer und Moore Beispiel 1.8. [Shift-Tabellen f ür Boyer/Moore] datenbank 999999991 kuckuck 3336661 retrieval 999999991 rokoko 662641 papa 2241 2. Suffix-B¨ aume 62 Trie 2 Suffix-Bäume compiler 88888881 • In diesem Abschnitt sollen Datenstrukturen zur Beschleunigung des exakten String-Matchings (Problem 1.1) untersucht werden. abrakadabra 77777771131 00 • Konkret liegt folgende Situation vor: Satz 1.5. Algorithmus 1.7 löst Problem 1.1(a) in Zeit O(n + m) und Platz O(m). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 61 – Gegeben ist ein (evtl. sehr langer) String text. – Man hat Anfragen an text gemäß Problem 1.1, d.h. es sind alle (oder ein) Vorkommen eines beliebigen Strings pat in text aufzufinden. – Gesucht ist eine Datenstruktur D, mit der nach einer Preprocessingphase für text solche Anfragen in sublinearer Zeit beantwortet werden können. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 63 2. Suffix-B¨ aume Trie Anforderungen an die Datenstruktur D: 2. Suffix-B¨ aume Trie mit der Eigenschaft: ∀pat ∈ P ∃k : pat = path(k) • Die Größe von D sollte linear in |text| sein. • D sollte effizient (möglichst in O(|text|)) aufgebaut werden können. a b • Problem 1.1 (exaktes String-Matching) sollte mit Hilfe von D in sublinearer Zeit gelöst werden können. b ☞ Grundidee: Man finde eine geeignete Datenstruktur zur Repräsentation von Wortmengen. Beispiel 2.1. [Trie] Für die Menge P = {ab, ba, babb, bb} von Strings ergibt sich der Trie: a b b b Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 64 2. Suffix-B¨ aume Trie Trie 2. Suffix-B¨ aume 66 Trie Stringsuche mit Hilfe eines Tries Definition 2.1. Es sei Σ ein endliches Alphabet. Ein Trie (auch ΣBaum genannt) ist ein Wurzelbaum mit den folgenden Eigenschaften: Es sei P eine Menge von Strings. Dann gilt: • T rie(P ) kann in linearer Zeit bezüglich der Gesamtlänge der in P enthaltenen Strings aufgebaut werden. (i) Die Kanten des Baumes sind mit Zeichen aus Σ markiert. (ii) Für jeden Knoten k des Baums und jedes Zeichen c ∈ Σ gibt es höchstens eine Kante, die von k ausgeht und mit c markiert ist. Für einen Knoten k in einem Trie bezeichnet path(k) den String, der sich aus der Folge der Kantenmarkierungen auf dem Pfad von der Wurzel nach k ergibt. Für eine Menge P von Strings bezeichnet T rie(P ) den kleinsten Trie Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 65 • Die Größe von T rie(P ) ist linear in der Gesamtlänge der Strings von P. • Es sei w ein beliebiger String. Dann kann mit Hilfe von T rie(P ) in O(|w|) geprüft werden, ob w ∈ P gilt. • Für jeden Trie gilt: Ausgangsgrad ≤ |Σ|. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 67 2. Suffix-B¨ aume Trie • Ist das Pattern erschöpft und der aktuelle Knoten markiert einen String aus P , so ist das Suchpattern in P . Trie Suffix-Trie Beispiel 2.2. Suche des Strings ba in dem Trie für die Menge P = {ab, ba, babb, bb}. • Man folgt ausgehend von der Wurzel sukzessive den Kanten des Tries, die mit dem Symbol pat[j] markiert sind. 2. Suffix-B¨ aume a b b a Definition 2.2. Es sei s = s[1] . . . s[n − 1]$ ein String. Dann ist der Suffix-Trie ST (s) der Baum b b ST (s) = T rie({s[1 . . . n], s[2 . . . n], . . . , s[n − 1 . . . n], $}) b wobei die Blätter p von ST (s) zusätzlich eine Markierung label(p) tragen, für die gilt: label(p) = i ⇐⇒ path(p) = s[i . . . n] Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 68 Trie Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 70 2. Suffix-B¨ aume Trie • Ein erster Lösungsansatz besteht darin, sämtliche Suffixe eines Textes mit Hilfe eines Tries darzustellen. a b c $ 7 • Allgemein kann ein Suffix von text Präfix eines anderen Suffixes von text sein. Dies würde dazu führen, daß nicht jeder Suffix mit einem Blatt des Tries assoziiert werden kann. • Deshalb wird die zusätzliche Voraussetzung eingeführt, daß text mit einem Sonderzeichen $ abgeschlossen sein muß, das ansonsten in text nicht vorkommt. b a c $ 6 a c Beispiel 2.3. [Suffix-Trie] Der Suffix-Trie für den String abcabc$ sieht folgendermaßen aus: $ b 5 a $ b c 4 c b $ 3 $ c 2 $ 1 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 69 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 71 2. Suffix-B¨ aume Trie • Es besteht eine Eins-zu-eins-Beziehung zwischen den Knoten von ST (s) und den verschiedenen Substrings von s. ¨ Positionsbaume 2. Suffix-B¨ aume Bemerkungen: • u = erfüllt Bedingung (i) für jedes s ∈ Σ+ und jedes i mit 1 ≤ i ≤ |s|. • Die Anzahl der Knoten von ST (s) ist u.U. quadratisch in |s|. • Für |s| ≥ 2 erfüllt u = Bedingung (ii) nicht. • Dies ist z.B. für Strings der Form ambm$ der Fall. Solche Strings der Länge 2m + 1 haben m2 + 4m + 2 verschiedene Substrings. • Nicht für alle s und alle i existiert ein Positionsidentifikator. ☞ Statt einen kompletten Suffix ab Position i in text nimmt man nur einen String auf, der die Position i in text eindeutig beschreibt. • Falls für String s und Position i ein Positionsidentifikator existiert, so ist er eindeutig bestimmt. Lemma 2.1. Bei Abschluß von s mit einem Sonderzeichen $, das in s nicht vorkommt, existiert für jede Position von s$ ein Positionsidentifikator. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 72 ¨ Positionsbaume 2. Suffix-B¨ aume Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 74 ¨ Positionsbaume 2. Suffix-B¨ aume Beispiel 2.4. Der String s = abcabc$ hat die Positionsidentifikatoren: Positionsidentifikator i 1 2 3 4 5 6 7 Definition 2.3. Es seien Σ ein Alphabet, s, u Zeichenreihen (Strings) ∈ Σ∗ mit s 6= und i ∈ IN mit 1 ≤ i ≤ |s|. Dann heißt u Positionsidentifikator für die Position i in s (Bezeichnung: u = pids(i)) genau dann, wenn die folgenden Bedingungen gelten: (i) ∃y, z mit s = yuz und |y| = i − 1. pids(i) abca bca ca abc$ bc$ c$ $ Korollar 2.2. s1 . . . sn+1 besitzt je einen Positionsidentifikator für i = 1, . . . , n + 1 ⇐⇒ sn+1 6∈ {s1, . . . , sn} (ii) ∀y 0, z 0 : s = y 0uz 0 ⇒ y 0 = y und z 0 = z (iii) |u| ist minimal unter den Bedingungen (i) und (ii). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 73 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 75 ¨ Positionsbaume 2. Suffix-B¨ aume Lemma 2.3. Kein Positionsidentifikator ist Anfang (Präfix) eines Positionsidentifikators für eine weitere Position in demselben String s$. ¨ Positionsbaume 2. Suffix-B¨ aume Beispiel 2.5. Der Positionsbaum für s = abcabc$ sieht wie folgt aus: Beweis. a b c $ • Sei i 6= j mit p(i) Anfang von p(j), 7 b • d.h. p(i)ω = p(j), a c 3 ⇒ p(i) kommt in s auch an Position j vor. c ⇒ p(i) identifiziert die Position i nicht eindeutig. a a $ 2 5 $ 6 $ ⇒ Widerspruch. 1 4 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 76 ¨ Positionsbaume 2. Suffix-B¨ aume Positionsbaum Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 78 ¨ Positionsbaume Lemma 2.4. Jeder String s$ hat einen eindeutigen Positionsbaum. Beweis. Definition 2.4. Es sei s = s[1] . . . s[n]$ ein String. Dann ist der Positionsbaum P T (s) der Baum P T (s) = T rie({pids(1), pids(2), . . . , pids(n + 1)}) • Für jede Position i gibt es einen eindeutigen Positionsidentifikator p(i). • Man braucht nun nur den Trie zu p(1), . . . , p(n + 1) aufzubauen. wobei die Blätter p von P T (s) zusätzlich eine Markierung label(p) tragen, für die gilt: • Die Eindeutigkeit folgt aus der Minimalität des Baumes. 2 label(p) = i ⇐⇒ path(p) = pids(i) Korollar 2.5. Ein Positionsbaum für s$ mit |s| = n hat n + 1 Blätter. Beweis. Folgt direkt aus Lemma 2.4. 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 77 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 79 2. Suffix-B¨ aume ¨ Positionsbaume Lemma 2.6. Es sei s 6= . Dann hat jedes Blatt im Positionsbaum von s$ mindestens einen Bruder. 2. Suffix-B¨ aume ¨ String-Matching mit Positionsbaumen Der Abstieg terminiert, falls: Beweis. (1) pat erschöpft und zwar • p(i) = ya, y ∈ Σ∗, a ∈ Σ (1.1) an einem inneren Knoten oder (1.2) an einem Blatt • Falls y = , so hat das mit i markierte Blatt einen Bruder, weil es noch mindestens eine weitere Position im Baum gibt. (2) pat nicht erschöpft, aber der Positionsbaum ist erschöpft und zwar (2.1) an einem inneren Knoten oder (2.2) an einem Blatt • Also sei |y| ≥ 1. ⇒ Dann kommt y noch an anderer Stelle, etwa j 6= i, im String s vor (wegen der Minimalität von p(i)). ⇒ Also muß es eine Bruderkante geben, die den Anfang des Weges zu dem mit j markierten Blatt bildet. 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 80 ¨ String-Matching mit Positionsbaumen Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 82 ¨ String-Matching mit Positionsbaumen Aus diesen unterschiedlichen Fällen der Terminierung ergeben sich folgende Resultate: String-Matching mit Hilfe von Positionsbäumen (1) pat kommt genau an den Positionen vor, die vom Abbruchknoten aus erreichbar sind. • Gegeben sei der Positionsbaum für text$. Insbesondere kommt im Fall (1.2) pat genau an der Position vor, mit der das Blatt markiert ist. • Es sei pat = p1, . . . , pm. • Steige im Positionsbaum ab gemäß pat, d.h. (2.1) pat kommt in text nicht vor. – wähle zunächst die von der Wurzel ausgehende und mit p1 markierte Kante, – dann die mit p2 markierte Kante, usw. (2.2) pat kommt höchstens an der Position vor, mit der das erreichte Blatt markiert ist. • Es treten nun verschiedene Fälle auf, die den Abstieg beenden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 81 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 83 ¨ String-Matching mit Positionsbaumen 2. Suffix-B¨ aume Beispiel 2.6. Es abcabc. sei text = • pat = bc: Fall 1.1, pat kommt genau ab den Stellen 2 und 5 vor. • pat = bcaa: Fall 2.2, pat kann höchstens an Position 2 vorkommen. Prüfung ergibt: pat kommt nicht vor. a b • pat = ca: Fall 1.2, pat kommt nur an der Stelle 3 vor. ¨ String-Matching mit Positionsbaumen • Der Aufwand für das Preprocessing (Konstruktion des Positionsbaums) wurde bisher nicht berücksichtigt. • Selbst ein hoher Preprocessingaufwand würde sich aber u.U. lohnen, wenn: – genügend Anfragen an text gestellt werden oder – wenn der Aufwand zu verschiedenen Zeiten verschieden teuer ist. $ 7 b • pat = ac: Fall 2.1, pat kommt in text nicht vor. • pat = cab: Fall 2.2, pat kann höchstens an Position 3 vorkommen. Prüfung ergibt: pat kommt vor. c 2. Suffix-B¨ aume a c 3 c a 1 a $ 2 5 $ 6 $ 4 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 84 ¨ String-Matching mit Positionsbaumen Komplexität: Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 86 ¨ Konstruktion von Positionsbaumen Ansätze zur Konstruktion von Positionsbäumen • Der Aufwand für die Suche beträgt O(|pat|), • mit Ausnahme von Fall 1.1, wo ein Unterbaum traversiert werden muß. • Es liegt die Vermutung nahe, daß im Falle 1.1 der Aufwand O(|pat| + k) beträgt, wobei k die Anzahl der Vorkommen von pat ist. • Alternative: man ermittelt für jeden Knoten (oder jeden ab einer gewissen Tiefe) die Marken der von diesem Knoten aus erreichbaren Blätter und legt diese als Liste in dem Knoten ab. Nachteil: erhöhter Speicherplatzverbrauch Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 85 Grundlegender Ansatz: Induktive Konstruktion, d.h. man konstruiert sukzessive P T (s1), . . . , P T (s1, . . . si), P T (s1 . . . sisi+1), . . . , P T (s1 . . . sn$). Problem: Die Zwischenbäume P T (s1 . . . si) brauchen nicht zu existieren. Beispiel: s = abab$. P T (s1s2s3) existiert nicht, da Position 3 keinen Pos.-Id. in aba hat. Wie kann man diese Schwierigkeiten umgehen? (a) Man konstruiert den Baum von rechts nach links (anstatt von links nach rechts), d.h. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 87 2. Suffix-B¨ aume ¨ Konstruktion von Positionsbaumen P Tn+1 = P T ($) P Tn = P T (sn$) P Tn−1 = P T (sn−1sn$) .. P Ti = P T (si . . . sn$) .. P T1 = P T (s1 . . . sn$) Bemerkung: Für jeden Teilstring si . . . sn$ existiert der zugehörige Positionsbaum. Ausgehend von dem Positionsbaum für si . . . sn$ konstruiert man den Positionsbaum für si−1si . . . sn$ wie folgt: Von der Wurzel aus durchläuft man einen Pfad mit den Markierungen si−1, si, . . ., bis man: P T (s1$) P T (s1s2$) .. P T (s1s2 . . . sn$) (a) ein Blatt k erreicht. – Das Blatt k sei mit j markiert. – Der bisherige Positionsidentifikator für j kommt also auch an der Stelle i − 1 vor. Nachteil: sehr hoher Aufwand 2. Suffix-B¨ aume 88 ¨ Konstruktion von Positionsbaumen (c) Man spiegelt den Text und die Definition des Begriffs Pos.-Id. und geht wie unter (a) vor. Nachteil: unnatürliche Umgehensweise (d) Man verzichtet darauf, daß die Zwischenbäume Positionsbäume sind. ☞ Wir untersuchen die Ansätze (a) und (d). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 90 ¨ Konstruktion von Positionsbaumen – Sowohl p(j) als auch p(i − 1) sind zu verlängern, bis sie sich unterscheiden. – Dementsprechend wird der Pfad von dem erreichten Blatt aus verlängert. – Es entstehen zwei neue Blätter mit den Markierungen i − 1 und j. (b) einen inneren Knoten k erreicht, von dem aus keine Kante mit der benötigten Markierung ausgeht. – Es ist ein neues Blatt k 0 an k anzuhängen. – Dieses Blatt erhält die Markierung i − 1. – Die Kantenmarkierung zur Kante (k, k 0) ist das letzte Zeichen von p(i − 1). • (a) : Rechts-Links-Konstruktion • (b) : Links-Rechts-Konstruktion Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 ¨ Konstruktion von Positionsbaumen Rechts-Links-Konstruktion von Positionsbäumen (b) Man hängt jeweils ein $-Zeichen an, d.h. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 89 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 91 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume • Ein Positionsbaum wächst also von einem Blatt aus weiter nach unten, Veranschaulichung für (a): PT(bcabc$) PT(abcabc$) a b c $ a b c – bis sich eine Unterscheidung der Positionsidentifikatoren ergibt – und der Pfad sich in zwei Blätter aufspaltet, $ 7 4 $ 2 5 b 6 3 c a 1 a $ 2 5 • oder von einem Zwischenknoten wächst ein neues Blatt heraus. $ a c 6 3 a 7 $ a c ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume Aufwandsabschätzung: • Der Gesamtaufwand zur Konstruktion eines Positionsbaums für einen String der Länge n ist: $ 4 O( n X |p(i)|) i=1 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 92 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume PT(bcabc$) PT(bbcabc$) b c $ a 7 4 a c 3 ¨ Konstruktion von Positionsbaumen b c • Nachteil der Rechts-Links-Konstruktion: Die gesamte Zeichenreihe muß bekannt sein, bevor sich der Positionsbaum konstruieren läßt. $ 7 4 b $ 6 2. Suffix-B¨ aume 94 Bemerkungen: Veranschaulichung für (b): a Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 a c 3 1 a $ a $ 2 5 2 5 $ • Beim Entwickeln von Text möchte man aber u.U. schon vorher Textstellen identifizieren und Korrekturen vornehmen. 6 • Einen Positionsbaum für den umgekehrten Text aufzubauen wäre unnatürlich. • Kann ein Positionsbaum auch effizient von links nach rechts konstruiert werden? Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 93 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 95 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume • Für Positionsbäume gilt eine sehr einfache und wichtige Verschachtelungseigenschaft, die beschreibt, wie sich Positionsidentifikatoren überlappen können. Links-Rechts-Konstruktion von Positionsbäumen • Ziel ist es, ein sogenanntes on-line Verfahren für die Konstruktion von Positionsbäumen zu entwickeln. • Da für Teilstrings der Positionsbaum nicht existiert, muß man partielle Positionsbäume betrachten. • Die wesentliche Aussage hierzu liefert das folgende MonotonieLemma. Lemma 2.7. [Monotonie-Lemma] Es sei e(i) die Position, bei der der Positionsidentifikator p(i) endet. Dann gilt: • Ein partieller Positionsbaum enthält für genau diejenigen Positionen des Textes einen Positionsidentifikator, für die es einen solchen Positionsidentifikator gibt. i<j =⇒ e(i) ≤ e(j) Beweis. Annahme: Es gibt in einem String s Positionen i < j mit e(i) > e(j). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 96 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume Beispiel 2.7. Der partielle Positionsbaum für abba sieht wie folgt aus: Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 98 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume Veranschaulichung: s a i b b a j e(j) e(i) • p(i) = si . . . se(i) ist der kürzestmögliche String, der die Position i eindeutig bestimmt. b • Somit kommt si+1 . . . se(i)−1 noch an anderer Stelle vor. 1 3 2 • sj . . . se(j) ist Substring von si+1 . . . se(i)−1. • Somit ist sj . . . se(j) nicht eindeutig in s. Widerspruch. 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 97 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 99 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume • Wir fassen Intervalle von Positionen zusammen, deren Pos.-Id. an derselben Stelle enden. Dies deuten wir so an: 2. Suffix-B¨ aume ¨ Konstruktion von Positionsbaumen Konstruktionsschritt: • Bei der Hinzunahme von si+1 wird nun versucht, die Markierung k im partiellen Positionsbaum weiter nach unten zu verschieben. text Intervalle von • Hierbei können die folgenden Fälle auftreten: die hier enden Positionen • Graphisch dargestellt ergibt sich dann die folgende Verschachtelung für die Positionsidentifikatoren: text $ Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 100 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume Prinzip der Links-Rechts-Konstruktion • Man nehme an, wir haben den partiellen Positionsbaum für den String s1 . . . si. • k sei die erste Postion, für die kein Positionsidentifikator existiert, d.h. sk sk+1 . . . si ist nicht eindeutig in s1 . . . si. s(1)s(2) . . . s(k−1) s(k) . . . s(i) • Der Anfang von p(k) ist sk sk+1 . . . si. (1) Der mit k markierte Knoten verfügt über eine ausgehende Kante für si+1. Dann bewegt man k längs der mit si+1 markierten Kante. Jetzt können die folgenden Fälle auftreten: (1.1) Der jetzt mit k markierte Knoten ist ein innerer Knoten. Dann ist nichts weiter zu tun. (1.2) Der jetzt mit k markierte Knoten ist ein Blatt mit Markierung l. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 102 ¨ Konstruktion von Positionsbaumen Dann ist p(l) um ein Zeichen zu verlängern, d.h. an das bisherige Blatt wird ein neuer Knoten angehängt und die Markierung l wandert zu dem neuen Blatt. (2) Der mit k markierte Knoten hat keine ausgehende Kante, die mit si+1 markiert ist. Dann wird ein neues Blatt und eine neue ausgehende und mit si+1 markierte Kante zu diesem Blatt angelegt. Das neue Blatt wird mit k markiert. Es gilt nun zu prüfen, ob Positionsidentifikatoren für die Positionen k + 1, k + 2, . . . existieren. Hierzu sind neue Blätter in den Baum einzutragen. Es sei k + m die erste Position für die nun kein Pos.-Id. existiert. Dann ist noch k + m einzutragen. • Diesem Anfang entspricht ein Knoten im partiellen Positionsbaum. Dieser Knoten sei mit k markiert. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 101 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 103 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume Fall 1.1: ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume Fall 2: s(i) s(i) s(i) s(i) k k s(i+1) s(i+1) s(i+1) k k Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 104 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume Fall 1.2: Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 106 ¨ Konstruktion von Positionsbaumen Bemerkungen: • Man kann den Eingabestring jederzeit mit einem $ abschließen. Dann wird mit dem angegebenen Verfahren der zugehörige Positionsbaum fertig konstruiert. s(i) • Folgende Kosten treten bei der Konstruktion auf: s(i) (1.1) p(k) wird um ein Zeichen verlängert. Aufwand: O(1) (1.2) p(k) und p(l) werden um je ein Zeichen verlängert. Aufwand: O(1) (2) p(k) wird um ein Zeichen verlängert. Aufwand: O(1) Das Einfügen von k + 1, k + 2, . . . k + m − 1 und das Auffinden von k + m kostet proportional zur Länge von p(k + 1) . . . p(k + m). k s(i+1) l s(i+1) k s( e(l) ) l Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 105 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 107 ¨ Konstruktion von Positionsbaumen 2. Suffix-B¨ aume • Der Gesamtaufwand zum Aufbau eines Positoinsbaums für einen String der Länge n ist also: O( n X |p(i)|) i=1 • Damit ist die Links-Rechts-Konstruktion nicht aufwendiger als die Rechts-Links-Konstruktion. • Sprachliche Texte s haben häufig die Eigenschaft, daß |p(i)| ≈ log |s| gilt. Dann ergibt sich für den Gesamtaufwand: 2. Suffix-B¨ aume Suffix-B¨ aume Definition 2.5. [Suffix-Baum] Es sei s = s[1] . . . s[n − 1]$ ein String. Dann ist der Suffix-Baum (suffix tree) SB(s) der Baum, der aus dem Suffix-Trie ST (s) auf die folgende Weise entsteht: (i) Jede Kantenfolge π, die nur aus Knoten mit Ausgangsgrad eins besteht, wird zu einer Kante e komprimiert. Es sei path0(π) der String, der sich aus Konkatenation der Kantenmarkierungen von π ergibt. (ii) Die Kante e erhält die Markierung label(e) = (i, j) mit path0(π) = s[i . . . j]. O(|s| log |s|) • Im Worst-Case beträgt der Aufwand O(n2). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 108 Suffix-B¨ aume Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 110 2. Suffix-B¨ aume Suffix-B¨ aume Beispiel 2.8. [Suffix-Baum] Der Suffix-Baum für s = abcabc$ sieht wie folgt aus: Suffix-Bäume abc Suffix-Bäume entstehen aus Suffix-Tries, (1,3) • indem Pfade nicht nur wie beim Positionsbaum von den Blätter her kompaktifiziert werden, • sondern sämtliche Teilpfade, die nur Knoten mit genau einem Sohn enthalten, auf eine Kante reduziert werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 bc 109 abc$ (4,7) 1 c $ (3,3) (2,3) (7,7) 7 abc$ $ (7,7) (4,7) 4 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 $ abc$ (7,7) (4,7) 5 3 $ (7,7) 6 111 2. Suffix-B¨ aume Suffix-B¨ aume Bemerkungen: • Kanten, die von dem gleichen Knoten ausgehen, unterscheiden sich im ersten Symbol der Markierung. • Somit ist bei einer Suche für das Branching, d.h. der Entscheidung, welchem Pfad man folgen soll, nur ein Zeichenvergleich erforderlich. • Alle internen Knoten haben einen Ausgangsgrad ≥ 2. Eine wesentliche Eigenschaft von Suffix-Bäumen ist, daß ihre Größe linear in der Länge des zugehörigen Strings ist. 2. Suffix-B¨ aume 112 Suffix-B¨ aume Lemma 2.8. Es sei text ein String. Dann ist die Anzahl der Knoten von SB(text) gegeben durch O(|text|). Satz 2.9. Gegeben sei ein String text. Liegt der Suffix-Baum SB(text) vor, dann kann in Zeit O(|text|) ein Preprocessing von SB(text) erfolgen, so daß die folgenden Anfragen für beliebige Strings w in Zeit O(|w|) beantwortet werden können: (i) Man finde das erste Vorkommen von w in text. (ii) Man finde das letzte Vorkommen von w in text. Weiterhin können sämtliche Vorkommen von w in text in Zeit O(|w| + k) aufgelistet werden, wobei k die Anzahl der Vorkommen von w in text ist. Beweis: Zu den Anfragen (i) bis (iii): Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 114 Suffix-B¨ aume • Das Preprocessing erfolgt bottom-up. • In jedem Knoten v werden die Werte für das erste Vorkommen f irst(v), das letzte Vorkommen last(v) und die Anzahl der Vorkommen count(v) des zugehörigen Strings path(v) abgelegt. Beweis. Es sei n = |text|. • ST (text) hat n Blätter. • In einem Blatt ist die Anzahl der Vorkommen gleich eins. Das erste und letzte Vorkommen ergibt sich durch die Knotenmarkierung. • Somit hat auch SB(text) n Blätter. • Da jeder innere Knoten von SB(text) einen Ausgangsgrad ≥ 2 hat, hat SB(text) höchstens n − 1 innere Knoten. • Somit hat SB(text) insgesamt höchstens 2n − 1 Knoten. 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Suffix-B¨ aume (iii) Man ermittle die Anzahl der Vorkommen von w in text. • Interne Knoten repräsentieren Substrings, die die größten gemeinsamen Präfixe zweier Suffixe sind. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 113 • In einem inneren Knoten gilt: – Das erste Vorkommen ist gleich dem Minimum der Werte für f irst der Söhne. – Das letzte Vorkommen ist gleich dem Maximum der Werte für last der Söhne. – Die Anzahl der Vorkommen ist gleich der Summe der Werte für count der Söhne. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 115 2. Suffix-B¨ aume Suffix-B¨ aume ⇒ Für einen String w kann die Antwort zu den ersten drei Fragen durch eine Top-Down-Suche in Zeit O(|w|) berechnet werden. 2. Suffix-B¨ aume Suffix-B¨ aume Satz 2.10. Ein Suffix-Baum für einen String text kann in Zeit O(|text|) aufgebaut werden. Beweis. Siehe Literatur, z.B. mit dem Algorithmus von McCreight. 2 Um alle Vorkommen von w zu ermitteln, geht man wie folgt vor: • Der zu w zugehörige Knoten v wird in Zeit O(|w|) ermittelt. • Von Knoten v aus wird der Baum traversiert, um alle Vorkommen zu ermitteln. • Wenn k die Anzahl der Vorkommen von w in text ist, dann hat der Unterbaum mit Wurzel v genau k Blätter. • Mit der gleichen Argumentation wie in Lemma 4.2 folgt, daß der Unterbaum mit Wurzel v insgesamt höchstens 2k − 1 Knoten hat. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 116 2. Suffix-B¨ aume Suffix-B¨ aume Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume • Somit ist die Traversierung des Unterbaums in O(k) möglich. ⇒ Der Gesamtaufwand beträgt O(|w| + k). 2 Das Problem des exakten String-Matchings wird wie folgt verallgemeinert: statt eines einzelnen Musters ist nun eine Menge von Mustern gegeben. Anfragepfad zu w Problem 2.1. [Matching von Wortmengen] Gegeben sei eine Menge P = {pat1, . . . , patk } von Strings und ein String text. Man bestimme, ob für ein i (mit 1 ≤ i ≤ k) pati ein Substring von text ist. first(v) = erstes Vorkommen von w Preprocessing last(v) = letztes Vorkommen von w count(v) = Anzahl der Vorkommen von w v Bemerkungen: • Im folgenden bezeichnet m := Strings in P . Unterbaum mit Wurzel v Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Matching von Wortmengen Matching von Wortmengen Veranschaulichung des Retrievals: Bottom-up 118 117 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Pk i=1 |pati| die Gesamtlänge der 119 2. Suffix-B¨ aume Matching von Wortmengen 2. Suffix-B¨ aume Matching von Wortmengen • Durch k-fache Anwendung des Algorithmus von Knuth, Morris Pratt bzw. Boyer-Moore kann Problem 2.1 in Zeit O(m+kn) gelöst werden. • Liegt der Automat für einen String pat vor, kann in O(n) geprüft werden, ob text in pat vorkommt. • Im folgenden wird gezeigt, daß Problem 2.1 in Zeit O(m + n) und Platz O(m) lösbar ist. • Der zugehörige Algorithmus simuliert hierzu einfach die Abarbeitung des Automaten. Grundidee zur Lösung von Problem 2.1: • wesentlicher Nachteil: i. allg. hat solch ein Automat O(|Σ|m) Zustandsübergänge. ☞ Man konstruiere einen deterministischen endlichen Automaten, der die Strings aus P erkennt. Dieser automatenbasierte Ansatz wird nun auf eine Menge von Strings verallgemeinert. ☞ Genauer: Man baue einen Automaten für die Sprache Σ∗{pat1, . . . , patk }Σ∗ Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 120 2. Suffix-B¨ aume Matching von Wortmengen Veranschaulichung: Für Σ = {a, b} und P = {abaaba} kann der folgende Automat verwendet werden: b 0 a a a b 1 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 122 Matching von Wortmengen Aho-Corasick Pattern-Matching-Automat a a 3 a 4 b 5 a Definition 2.6. Ein Aho-Corasick Pattern-Matching-Automat (ACAutomat) besteht aus den folgenden Komponenten: 6 b b (i) Einer endlichen Menge Q von Zuständen, b (ii) einem endlichen Eingabealphabet Σ, b Bemerkungen: (iii) einer Transitionsfunktion g : Q × Σ −→ Q ∪ {f ail}, • pat = abaaba ist genau dann in text enthalten, wenn bei Anwendung des obigen Automaten auf text irgendwann der Zustand 6 erreicht wird. (iv) einer Fehlerfunktion h : Q −→ Q ∪ {f ail}, Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 121 (v) einem initialen Zustand q0 ∈ Q und Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 123 2. Suffix-B¨ aume Matching von Wortmengen (vi) einer Menge F ⊂ Q von Endzuständen. 2. Suffix-B¨ aume Matching von Wortmengen Beispiel 2.9. [AC-Automat f ür einelementiges P ] Es sei P = {abaaba}. Der AC-Automat sieht dann folgendermaßen aus: Solch ein AC-Automat wird wie folgt zur Lösung von Problem 2.1 eingesetzt: 0 a 1 b 2 a 3 a 4 b 5 a 6 Algorithmus 2.1. [Algorithmus von Aho-Corasick] q := q0 for i := 1 to n do while q 6= f ail and g(q, text[i]) = f ail do q := h(q) end if q = f ail then q := q0 else q := g(q, text[i]) endif if q ∈ F then return true end return false Transitionsfunktion g Fehlerfunktion h Bemerkungen: • Die Fehlerfunktion h entspricht der Tabelle border aus dem Algorith- Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 124 Matching von Wortmengen Bemerkungen: Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 126 Matching von Wortmengen mus von Morris und Pratt. • Für jedes Symbol von text wird zunächst versucht, der Transitionsfunktion g zu folgen. • Ist dies nicht möglich, wird die Fehlerfunktion h angewendet, bis ein Zustandsübergang möglich ist. • Durch die Fehlerfunktion ist die Größe des AC-Automaten O(m) und somit unabhängig von |Σ|. Die meisten Komponenten eines AC-Automaten ergeben sich aus dem Trie für die Menge P . Konsequenzen für die Konstruktion des AC-Automaten für P : • Findet man auch mit h keinen möglichen Zustandsübergang, beginnt man wieder von vorne. • Die Zustandsmenge Q entspricht den Knoten von T rie(P ). • q0 ist die Wurzel von T rie(P ). • Die Transitionsfunktion g ergibt sich aus den Kanten von T rie(P ) und der Bedingung: g(q, c) = f ail genau dann, wenn keine mit c markierte Kante von dem Knoten q ausgeht. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 125 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 127 2. Suffix-B¨ aume Matching von Wortmengen 2. Suffix-B¨ aume Matching von Wortmengen • Die Endzustände ergeben sich aus der folgenden Bedingung: 1 a q ∈ Q ist genau dann ein Endzustand, wenn ein pat ∈ P existiert mit pat ist Suffix von path(q). b 2 (i) Jedes Blatt von T rie(P ) ist ein Endzustand. (Beispiel: Knoten 3) (ii) Jeder innere Knoten q mit path(q) ∈ P ist ein Endzustand. Solche Endzustände treten auf, wenn ein pati ∈ P echter Präfix eines patj ∈ P ist. (Beispiel: Knoten 5) (iii) Jeder innere Knoten q für den ein pat ∈ P existiert mit pat ist echter Suffix von path(q) ist ein Endzustand. Solche Endzustände treten auf, wenn ein pati ∈ P Substring und nicht echter Suffix oder Präfix eines patj ∈ P ist. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 4 b Genauer: es ist gibt drei disjunkte Klassen von Endzuständen: 128 Matching von Wortmengen a 3 5 b 8 b 6 Transitionsfunktion g b Fehlerfunktion h 7 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 130 Matching von Wortmengen Bemerkungen: Anders ausgedrückt: pati tritt im Innern von patj auf. (Beispiel: Knoten 6) Die Endzustände, die unter (i) oder (ii) fallen, können beim Aufbau von T rie(P ) direkt erkannt werden. • Die Fehlerfunktion stellt die Verallgemeinerung von border auf eine Menge von Strings dar. Es bleibt das Problem, die Fehlerfunktion h zu berechnen und Endzustände der Klasse (iii) zu erkennen. • Durch die Maximalität in der Definition der Fehlerfunktion wird sichergestellt, daß kein Vorkommen eines Musters übersehen wird (entspricht der kleinsten Verschiebung). Die Fehlerfunktion h des AC-Automaten wird wie folgt definiert: h(q) = q 0 gilt genau dann, wenn q 0 unter den Knoten von T rie(P ) den längsten echten Suffix von path(q) liefert. Falls kein solches q 0 existiert, gelte h(q) = f ail. • Die Fehlerfunktion geht im Baum stets nach oben. Algorithmus 2.2. [Berechnung der Fehlerfunktion eines AC-Automaten] Beispiel 2.10. [Fehlerfunktion eines AC-Automaten] Es sei P {ab, ba, babb, bb}. = h(q0) := f ail forall q mit q ist Sohn von q0 do h(q) := q0 end Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 129 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 131 2. Suffix-B¨ aume 0 Matching von Wortmengen 2. Suffix-B¨ aume 0 d.h., path(q) entspricht dem längsten Präfix eines Patterns aus P , der mit einem Suffix des bisher gelesenen Teils von text (nämlich text[1 . . . i]) übereinstimmt. – Es sei i die erste Position von text, an der ein Vorkommen eines Strings aus P endet. Von den Strings aus P , die an i enden, sei patj der längste. i0 ≤ i sei die Position, an der patj beginnt. q sei der Zustand des AC-Automaten nach der Bearbeitung von text[i]. – Man kann zwei Fälle unterscheiden: (i): Es existiert i00 < i0 mit text[i00 . . . i] ist echter Präfix eines Patterns patj 00 aus P . Falls mehrere solche i00 existieren, betrachte man deren Maximum. Es sei l := i − i00 + 1 die Länge des zu patj 00 gehörigen Präfixes. Aus der Invariante und der Maximalität von i00 folgt: path(q) = patj 00 [1 . . . l]. forall q mit depth(q ) ≥ 2 in BFS-Reihenfolge do q sei der Vater von q 0 c sei das Symbol mit g(q, c) = q 0 r := h(q) while r 6= f ail and g(r, c) = f ail do r := h(r) end if r = f ail then h(q 0) := g(q0, c) else h(q 0) := g(r, c) if h(q 0) ist Endzustand then nimm q 0 in die Menge der Endzustände auf endif endif end Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume Matching von Wortmengen 132 Matching von Wortmengen Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 2. Suffix-B¨ aume 134 Matching von Wortmengen Nach Konstruktion ist patj in patj 00 enthalten, und somit ist q ein Endzustand der Klasse (iii). (ii): Gilt (i) nicht, dann ist gemäß der Invariante patj der längste erkannte Präfix, und q ist ein Endzustand der Klasse (i) oder (ii). Lemma 2.11. Algorithmus 2.10 berechnet die Fehlerfunktion eines AC-Automaten in Zeit O(m). Satz 2.12. Problem 2.1 kann mit dem Algorithmus von Aho-Corasick in Zeit O(n + m) und Platz O(m) gelöst werden. • Laufzeit und Platz Beweis. – Preprocessing in O(m). – Platz wird nur für den Trie benötigt. – Die Transitionsfunktion g wird höchstens n mal erfolgreich angewendet. – Bei jeder erfolgreichen Anwendung von g geht man im Trie um genau eine Stufe nach unten. – Die Fehlerfunktion h geht im Baum stets nach oben. – Somit kann h höchstens n mal angewendet werden. • Korrektheit – Gemäß der Definition der Fehlerfunktion h gilt in Algorithmus 2.9 am Ende der Schleife: path(q) = text[i − depth(q) + 1 . . . i] und depth(q) = max{ l |patj [1 . . . l] = text[i − l + 1 . . . i] (1 ≤ j ≤ k)} Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 133 2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 135 ¨ Ausdr ¨ucke Matching regularer 3. Approximatives String-Matching 3 Matching regulärer Ausdrücke und approximatives String-Matching ¨ Ausdr ¨ucke Matching regularer 3. Approximatives String-Matching Vorgehensweise: • Aus r wird ein nichtdeterministischer endlicher Automat (NEA) mit -Übergängen konstruiert. Statt einer endlichen Anzahl von Mustern möchte man eventuell ein Musterschema vorgeben. Zur Spezifikation von Musterschemata eignen sich reguläre Ausdrücke. Problem 3.1. [Matching regulärer Ausdr ücke] Gegeben seien ein regulärer Ausdruck r und ein String text. L(r) bezeichne die durch r definierte Sprache. Man bestimme, ob ein Substring s von text existiert mit s ∈ L(r). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 – – – – bei dem die Knoten die Zustände darstellen, jede Kante mit einem Symbol aus Σ ∪ {} markiert ist, ein Zustand als initialer Zustand ausgezeichnet ist und einige Zustände Endzustände sind. • Ein NEA akzeptiert einen String, wenn ein Pfad vom initialen Zustand zu einem Endzustand existiert, für den die Konkatenation der beteiligten Kanten den String ergibt. 136 ¨ Ausdr ¨ucke Matching regularer 3. Approximatives String-Matching • Ein NEA mit -Übergängen ist ein gerichteter Graph, • m bezeichnet im folgenden die Länge von r. • Durch einen regulären Ausdruck ist u.U. eine unendliche Menge von Mustern gegeben. • Grundidee zur Lösung von Problem 3.1: Man konstruiere einen Automaten zur Erkennung der Sprache Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 138 ¨ Ausdr ¨ucke Matching regularer 3. Approximatives String-Matching Die Konstruktion des NEA erfolgt analog zum Beweis des Satzes über die Äquivalenz von regulären Ausdrücken und endlichen Automaten (vgl. Satz 2.3 bei Hopcroft/Ullman). Sie basiert auf den folgenden fünf Regeln: (1) Für die elementaren regulären Ausdrücke ∅, und c ∈ Σ werden die folgenden Automaten konstruiert: Σ∗L(r) ε c • Hier sind verschiedene Ansätze zur Automatenkonstruktion sinnvoll: (i) Konstruktion eines nichtdeterministischen Automaten oder (ii) eines deterministischen Automaten. • Im folgenden wird nur Ansatz (i) näher untersucht. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 137 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 139 ¨ Ausdr ¨ucke Matching regularer 3. Approximatives String-Matching (2) Aus den Automaten N1 und N2 für die regulären Ausdrücke r1 und r2 wird für r1|r2 der folgende Automat konstruiert: N1 ε ¨ Ausdr ¨ucke Matching regularer Bemerkungen: • Der nach den Regeln (1) bis (5) für r konstruierte Automat N hat höchstens 2m Zustände, denn jede Regel erzeugt nicht mehr als zwei neue Zustände. ε ε 3. Approximatives String-Matching ε • Die Regeln (1) bis (5) benötigen bei der Konstruktion jeweils nur konstante Zeit. N2 • N hat genau einen Start- und genau einen Endzustand. (3) Aus den Automaten N1 und N2 für die regulären Ausdrücke r1 und r2 wird für r1r2 der folgende Automat konstruiert: N1 ε N2 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 140 ¨ Ausdr ¨ucke Matching regularer 3. Approximatives String-Matching (4) Aus dem Automaten N für den regulären Ausdruck r wird für r ∗ der folgende Automat konstruiert: ε N ε Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 142 ¨ Ausdr ¨ucke Matching regularer • Jeder Zustand hat entweder – genau eine ausgehende Kante, die mit einem Symbol c ∈ Σ markiert ist oder – höchstens zwei mit markierte ausgehende Kanten. Als Konsequenz ergibt sich das folgende Lemma. ε ε Lemma 3.1. Zu einem regulären Ausdruck r kann in O(m) ein äquivalenter NEA mit -Übergängen konstruiert werden. (5) Für den regulären Ausdruck (r) wird der Automat von r genutzt. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 • Der Startzustand hat keine eingehende Kante, und der Endzustand hat keine ausgehende Kante. 141 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 143 ¨ Ausdr ¨ucke Matching regularer 3. Approximatives String-Matching ∗ Beispiel 3.1. Für den regulären Ausdruck (a|b) aba ergibt sich der folgende NEA: ε a ε ε ε ε ε b a b a 3. Approximatives String-Matching ¨ Ausdr ¨ucke Matching regularer Q := eps({qstart}) if qend ∈ Q then return true for i := 1 to n do Q := eps(g(Q, text[i]) ∪ {qstart}) if qend ∈ Q then return true end return false Lemma 3.2. Es sei r ein regulärer Ausdruck und N der zugehörige NEA. Dann kann mit Algorithmus 3.1 in Zeit O(nm) geprüft werden, ob ein String text einen auf r passenden Substring enthält. ε ε Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 144 ¨ Ausdr ¨ucke Matching regularer Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching Algorithmus 3.1. [Simulation eines NEA] Beweis. • Im folgenden Algorithmus bezeichnet qstart den Startzustand und qend den Endzustand. • N hat O(m) viele Zustände. 146 ¨ Ausdr ¨ucke Matching regularer • Q kann über einen Bitvektor repräsentiert werden. • Q ist eine Menge von Zuständen. • Jeder Zustand hat höchstens zwei ausgehende Kanten. • g(Q, c) bezeichnet die Menge aller Zustände, die von einem Zustand ∈ Q aus über eine mit c markierte Kante erreichbar sind. ⇒ g(Q, text[i]) und eps(Q) können in O(m) berechnet werden. • eps(Q) bezeichnet alle Zustände, die von einem Zustand ∈ Q aus über mit markierte Kanten erreichbar sind. Aus Lemma 3.1 und Lemma 3.2 ergibt sich: • Da das Muster an einer beliebigen Stelle des Textes auftreten kann, wird in jeder Iteration der Startzustand qstart zur aktuellen Zustandsmenge hinzugenommen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 145 2 Satz 3.3. Problem 3.1 kann in Zeit O(nm) und Platz O(m) gelöst werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 147 3. Approximatives String-Matching ¨ Ausdr ¨ucke Matching regularer Ein anderer Ansatz zum Matching regulärer Ausdrücke besteht darin, statt eines NEA einen deterministischen Automaten zu verwenden. 3. Approximatives String-Matching String-Metriken Anwendungsbeispiele: • Molekularbiologie (Erkennung von DNA-Sequenzen) Bemerkungen: • Ein NEA kann stets in einen äquivalenten deterministischen Automaten überführt werden (siehe “Theoretische Informatik”). • Mit einem deterministischen Automaten könnte die Erkennung eines Musters in Zeit O(n) erfolgen. • Problem: Die Größe eines deterministischen Automaten ist im worst case exponentiell in m. • Ausgleich verschiedener Schreibweisen (Grafik vs. Graphik) • Ausgleich von Beugungen • Toleranz gegenüber Tippfehlern • Toleranz gegenüber OCR-Fehlern Satz 3.4. Mit einem deterministischen Automaten kann Problem 3.1 in Zeit O(2m + n) und Platz O(2m) gelöst werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 148 String-Metriken Approximatives String-Matching Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 150 String-Metriken String-Metriken Der Begriff “nahezu” wird dabei durch eine Metrik auf Strings formalisiert. • In den Kapiteln 1 und 2 waren die Probleme derart, daß das Muster pat exakt mit einem Substring von text übereinstimmen mußte. Zur Erinnerung: Sei M eine Menge. Eine Funktion d : M × M −→ IR heißt Metrik, wenn die folgenden Bedingungen erfüllt sind: • Beim Matching von regulären Ausdrücken ließ man zwar Varianten zu, aber ebenfalls keine Fehler. • d(x, y) ≥ 0 für alle x, y ∈ M • In vielen praktischen Fällen ist es wünschenswert, die Stellen von text zu finden, die mit pat “nahezu” übereinstimme, d.h. man erlaubt Abweichungen zwischen pat und text. • d(x, y) = 0 ⇔ x = y für alle x, y ∈ M • d(x, y) = d(y, x) für alle x, y ∈ M • d(x, z) ≤ d(x, y) + d(y, z) für alle x, y, z ∈ M . (M, d) ist dann ein metrischer Raum. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 149 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 151 3. Approximatives String-Matching String-Metriken Problem 3.2. Gegeben seien ein String pat, ein String text, eine Metrik d für Strings und ein ganze Zahl k ≥ 0. Man finde alle Substrings y von text mit d(pat, y) ≤ k. 3. Approximatives String-Matching String-Metriken Editierdistanz, Levenstein-Metrik Definition 3.2. Bemerkungen: • Für zwei Strings x und y ist die Editierdistanz (Edit Distance) edit(x, y) definiert als die kleinste Anzahl an Einfüge- und Löschoperationen, die notwendig sind, um x in y zu überführen. • Für k = 0 erhält man Problem 1.1(b). • Problem 3.2 ist zunächst ein “abstraktes” Problem, da nichts über die Metrik d ausgesagt wird. • Zur Konkretisierung von Problem 3.2 und zur Entwicklung von entsprechenden Algorithmen müssen zunächst sinnvolle Metriken betrachtet werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 152 String-Metriken • Läßt man zusätzlich auch die Ersetzung eines Symbols zu, so spricht man von einer Levenstein-Metrik (Levenshtein Distance) lev(x, y). • Nimmt man als weitere Operation die Transposition (Vertauschung zweier benachbarter Symbole) hinzu, so erhält man die DamerauLevenstein-Metrik dlev(x, y). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 154 String-Metriken Bemerkungen: Hamming-Distanz Definition 3.1. Für zwei Strings x und y mit |x| = |y| = m ergibt sich die Hamming-Distanz (Hamming Distance) durch: d(x, y) = |{1 ≤ i ≤ m|x[i] 6= y[i]}| • Offensichtlich gilt stets dlev(x, y) ≤ lev(x, y) ≤ edit(x, y). • Die Damerau-Levenstein-Metrik wurde speziell zur Tippfehlerkorrektur entworfen. Bemerkungen: • Die Hamming-Distanz ist die Anzahl der Positionen, an denen sich x und y unterscheiden. Sie ist nur für Strings gleicher Länge definiert. • Wird in Problem 3.2 für d eine der Metriken aus Definition 3.2 verwendet, dann spricht man auch von “string matching with k differences” bzw. von “string matching with k errors”. • Wird in Problem 3.2 für d die Hamming-Distanz verwendet, so spricht man auch von “string matching with k mismatches”. Beispiel 3.2. Die Hamming-Distanz der Strings abcabb und cbacba beträgt 4. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 153 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 155 3. Approximatives String-Matching String-Metriken 3. Approximatives String-Matching Berechnung der Stringdistanz • Da die Metriken edit, lev und dlev sehr ähnlich sind, wird im folgenden nur die Levenstein-Metrik betrachtet. Beispiel 3.3. Für x = abcabba und y = cbabac gilt: edit(x, y) = 5 • Algorithmen für die anderen Metriken erhält man durch einfache Modifikationen der folgenden Verfahren. abcabba −→ bcabba −→ cabba −→ cbba −→ cbaba −→ cbabac dlev(x, y) = lev(x, y) = 4 • Im folgenden sei m = |x| und n = |y| und es gelte m ≤ n. abcabba −→ cbcabba −→ cbabba −→ cbaba −→ cbabac ☞ Lösungsansatz: dynamische Programmierung abcabba −→ bcabba −→ cbabba −→ cbabab −→ cbabac ☞ genauer: berechne die Distanz der Teilstrings x[1 . . . i] und y[1 . . . j] auf der Basis bereits berechneter Distanzen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 156 Berechnung der Stringdistanz Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 158 Berechnung der Stringdistanz Die Tabelle LEV sei definiert durch: Berechnung der Stringdistanz LEV [i, j] := lev(x[1 . . . i], y[1 . . . j]) mit 0 ≤ i ≤ m, 0 ≤ j ≤ n Problem 3.3. Gegeben seien zwei Strings x und y. Man ermittle edit(x, y) bzw. lev(x, y) bzw. dlev(x, y) sowie die zugehörigen Operationen zur Überführung der Strings. Die Werte für LEV [i, j] können mit Hilfe der folgenden Rekursionsformeln berechnet werden: Bemerkungen: • LEV [0, j] = j für 0 ≤ j ≤ n, LEV [i, 0] = i für 0 ≤ i ≤ m • Wenn x und y Dateien repräsentieren, wobei x[i] bzw. y[j] die i-te Zeile bzw. j-te Zeile darstellt, dann spricht man auch vom File Difference Problem. • Unter U NIX steht das Kommando diff zur Lösung des File Difference Problems zur Verfügung. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 157 • LEV [i, j] = min{ LEV [i − 1, j] + 1, LEV [i, j − 1] + 1, LEV [i − 1, j − 1] + δ(x[i], y[j])} 0 falls a = b • δ(a, b) = 1 sonst Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 159 3. Approximatives String-Matching Berechnung der Stringdistanz 3. Approximatives String-Matching Berechnung der Stringdistanz Beispiel 3.4. Darstellung von LEV als Matrix für x = cbabac und y = abcabbbaa: Bemerkungen: • Die Rekursionsformel spiegelt die drei Operation Löschen, Einfügen und Substitution wider. c b a b a c • Die Stringdistanz ergibt sich als LEV [m, n]. • Möchte man nur die Stringdistanz berechnen, so genügt es, sich auf Stufe i der Rekursion die Werte von LEV der Stufe i − 1 zu merken. • Benötigt man die zugehörigen Operationen, speichert man LEV als Matrix und ermittelt die zugehörigen Operationen in einer “Rückwärtsrechnung”. 0 1 2 3 4 5 6 a 1 1 2 2 3 4 5 b 2 2 1 2 2 3 4 c 3 2 2 2 3 3 3 a 4 3 3 2 3 3 4 b 5 4 3 3 2 3 4 b 6 5 4 4 3 3 4 b 7 6 5 5 4 4 4 a 8 7 6 5 5 4 5 a 9 8 7 6 6 5 5 Die zugehörigen Umwandlungen lauten: cbabac −→ ababac −→ abcabac −→ abcabbac −→ abcabbbac −→ abcabbbaa Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 160 Berechnung der Stringdistanz Algorithmus 3.2. [Berechnung der Stringdistanz] Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 162 3. Approximatives String-Matching Berechnung der Stringdistanz Veranschaulichung: Die Berechnung der Stringdistanz kann als Pfad in einem Graphen veranschaulicht werden. for i := 0 to m do LEV [i, 0] := i end for j := 1 to n do LEV [0, j] := j end for i := 1 to m do for j := 1 to n do LEV [i, j] := min{ LEV [i − 1, j] + 1, LEV [i, j − 1] + 1, LEV [i − 1, j − 1] + δ(x[i], y[j])} end end return LEV [m, n] a c b c a b b b a a = Löschoperation Einfügeoperation b a b = Substitution = a = = keine Änderung c Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 161 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 163 3. Approximatives String-Matching Berechnung der Stringdistanz Der dargestellte Pfad entspricht der folgenden (nicht optimalen) Umwandlung: cbabac −→ acbabac −→ abcbabac −→ abcabac −→ abcabbac −→ abcabbbac −→ abcabbbaa Aus der Rekursionsformel und den Bemerkungen folgt: Satz 3.5. Die Stringdistanz (für edit, lev und dlev) kann in Zeit O(mn) und Platz O(m) berechnet werden. Problem 3.3 kann mit Platz O(mn) gelöst werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 3. Approximatives String-Matching 164 Berechnung der Stringdistanz Mit einer kleinen Änderung kann die angegebene Rekursionsformel auch zur Lösung von Problem 3.2 eingesetzt werden. Es sei M LEV definiert durch: M LEV [i, j] := min {lev(pat[1 . . . i], text[l . . . j])} 1≤l≤j d.h., M LEV [i, j] ist die kleinste Distanz zwischen pat[1 . . . i] und einem Suffix von text[1, j]. Es gilt nun: M LEV [0, j] = 0 für 0 ≤ j ≤ n, denn pat[1 . . . 0] = und ist stets in text[1 . . . j] ohne Fehler enthalten. 3. Approximatives String-Matching Berechnung der Stringdistanz Ansonsten berechnet sich M LEV [i, j] wie LEV [i, j], d.h.: • M LEV [i, 0] = i für 0 ≤ i ≤ m • M LEV [i, j] = min{ M LEV [i − 1, j] + 1, M LEV [i, j − 1] + 1, M LEV [i − 1, j − 1] + δ(x[i], y[j])} Gilt nun M LEV [m, j] ≤ k, so endet in Position j ein Substring y von text mit lev(pat, y) ≤ k (wobei m die Patternlänge ist). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 166 3. Approximatives String-Matching Berechnung der Stringdistanz Beispiel 3.5. Die Tabelle M LEV für pat = ABCDE und text = ACEABP CQDEABCR. i 0 1 2 3 4 5 j 0 A B C D E 0 1 2 3 4 5 1 A 0 0 1 2 3 4 2 C 0 1 1 1 2 3 3 E 0 1 2 2 2 2 4 A 0 0 1 2 3 3 5 B 0 1 0 1 2 3 6 P 0 1 1 1 2 3 7 C 0 1 2 1 2 3 8 Q 0 1 2 2 2 3 9 D 0 1 2 3 2 3 10 E 0 1 2 3 3 2 11 A 0 0 1 2 3 3 12 B 0 1 0 1 2 3 13 C 0 1 1 0 1 2 Für k = 2 ergeben sich die Positionen 3, 10, 13 und 14. Die zugehörigen Substrings von text sind ACE, ABPCQDE, ABC und ABCR. Satz 3.6. Problem 3.2 kann für die Metriken edit, lev und dlev in Zeit O(mn) gelöst werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 165 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 167 14 R 0 1 2 1 1 2 4. Lexikalische Analyse und Parsing Lexikalische Analyse 4 Lexikalische Analyse und Parsing Lexikalische Analyse Aufgaben bei der Compilierung • Lexikalische Analyse und Parsing sind die wichtigsten Bereiche des Compilerbaus. • Prinzipien und Techniken des Compilerbaus beschränken sich nicht auf die Erstellung von Übersetzern für Programmiersprachen. • Compilerbau-Techniken finden generell dort Anwendung, wo Zeichenfolgen sequentiell verarbeitet werden, um hieraus abgeleitete Informationen oder Aktionen zu erzeugen. • In diesem Kapitel werden wir insbesondere allgemein verwendbare Compilerbau-Techniken kennenlernen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 4. Lexikalische Analyse und Parsing 168 Lexikalische Analyse Beispiele f ür die Anwendung von Compilerbau-Techniken: 1. Lexikalische Analyse (Scanning) • Der Strom von Zeichen, aus denen ein Text besteht, wird sequentiell gelesen und in Symbole (Tokens) separiert. • Ein Symbol stellt eine Zeichenfolge dar, die eine bestimmte Bedeutung hat. • Beispiel: Die lexikalische Analyse teilt die Zeile summe = gebuehr + rate * 12; wie folgt auf: Bezeichner: summe, Operator: =, Bezeichner: gebuehr, Operator: +, Bezeichner: rate, Operator: *, Ganzzahl-Literal: 12, Trenner: ; Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 170 4. Lexikalische Analyse und Parsing Lexikalische Analyse 2. Syntaxanalyse (Parsing) Zuweisung • Pretty-Printer (Programmtext −→ formatierter Programmtext) • Anfrage-Interpreter, Shells (Befehle −→ Aktionen) • Dokumentationswerkzeuge (javadoc, kdoc) (Programmtext −→ HTML-Seiten) • Indexierer für Web-Seiten (HTML-Seiten −→ Datenbankeinträge) • XML-Prozessoren (XML-Dokumente −→ Aktionen für die Verarbeitung) • Symbole des Textes werden Bezeichner zu grammatikalischen Einheiten zusammengefaßt. summe = Ausdruck Ausdruck + Bezeichner Ausdruck • Diese Zusammenfassung wird durch Syntaxregeln bestimmt. • Dies kann durch einen Syntaxbaum veranschaulicht werden. gebuehr Bezeichner rate Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 169 Ausdruck Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 * Ausdruck Integer 12 171 4. Lexikalische Analyse und Parsing Lexikalische Analyse 4. Lexikalische Analyse und Parsing Lexikalische Analyse Warum teilt man lexikalische Analyse und syntaktische Analyse auf? 3. Semantische Analyse • Der Text wird auf semantische Fehler hin untersucht. Beispiele: – Verwendung von Bezeichnern – Typüberprüfungen • Bestimmung von Typ-Informationen, um die anschließende Phase der Synthese vorzubereiten. • Erzeugung einer Zwischendarstellung • Vereinfachung des Entwurfs Eine Grammatik, die direkt auf den Eingabezeichen basiert, wäre typischerweise mit vielen Konflikten behaftet. Die Trennung führt auch zu einem klareren Sprachentwurf. • Verbesserung der Effizienz des Compilers 4. Synthese Ist der Scanner getrennt von den anderen Teilen der Compilierung, kann er spezifischer und effizienter implementiert werden. • Auswertung der Zwischendarstellung • Optimierung • Transformation der Zwischendarstellung in die Enddarstellung • Die Portabilität von Compilern wird verbessert. Besonderheiten des Eingabealphabets und andere spezifische Eigenarten können auf den Scanner beschränkt werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 172 4. Lexikalische Analyse und Parsing Lexikalische Analyse Lexikalische Analyse Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 174 Lexikalische Analyse Grundproblem: Wie fassen wir einzelne Zeichen zu Eingabesymbolen zusammen? • Die lexikalische Analyse bildet die erste Phase der Compilierung. • Ihre Hauptaufgabe besteht darin, Eingabezeichen zu lesen und als Ausgabe eine Folge von Symbolen zu erzeugen, die der Parser syntaktisch analysiert. Symbol Zwischendarstellung Text Scanner • Man könnte einen Automaten für ein bestimmtes Erkennungsproblem entwerfen. Parser • Solch einen Automaten kann man leicht in ein entsprechendes Programm überführen. gib naechstes Symbol ☞ Allgemeines Problem: Spezifikation und Entwurf von Programmen, deren Aktionen durch bestimmte Textmuster ausgelöst werden. Symbol− tabelle Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 • Lexikalische Analyse ist ein klassischer Anwendungsbereich für die Automatentheorie. 173 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 175 4. Lexikalische Analyse und Parsing Lexikalische Analyse 4. Lexikalische Analyse und Parsing Beispiel 4.1. Ein Automat zur Erkennung und Ausgabe von Kommentaren im Stil von C: 6= / 6= * Reguläre Ausdrücke Zur Spezifikation von Mustern verwenden wir reguläre Ausdrücke mit den üblichen Operationen Konkatenation, Auswahl (|) und Wiederholung (*). / Wir vereinbaren die folgenden Prioritäten: * / [gespeicherte Zeichen ausgeben] Lexikalische Analyse 1. Die Wiederholung * hat die höchste Priorität. * 6= * [Zeichen speichern] 2. Die Konkatenation hat die zweithöchste Priorität. 6 / = [* und Zeichen speichern] 3. Die Auswahl hat die niedrigste Priorität Alle Operationen sind links-assoziativ. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 176 Lexikalische Analyse • Mit dem Automat geben wir an, wie ein Muster erkannt werden soll. • Einfacher wäre es, wenn wir nur angeben müssten, was wir erkennen wollen. • Wir bräuchten also eine geeignete Spezifikationssprache für zu erkennende Muster. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing • Diese Namen können dann in folgenden regulären Ausdrücken wie Symbole verwendet werden. Definition 4.1. Es sei Σ ein Alphabet. Eine reguläre Definition ist eine Folge von Definitionen der Form d1 → r 1 d2 → r 2 .. dn → r n • Zu regulären Ausdrücken können die entsprechenden Automaten zur Erkennung automatisch konstruiert werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Lexikalische Analyse • Zur Vereinfachung der Schreibweise ist es vorteilhaft, regulären Ausdrücken Namen zu geben. • Hierfür eignen sich als Grundkonstrukt reguläre Ausdrücke. • Diese Automaten müssen dann geeignet simuliert werden. 178 Dabei ist di ein eindeutiger Name und ri ein regulärer Ausdruck über den Symbolen aus Σ ∪ {d1, . . . , di−1}. 177 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 179 4. Lexikalische Analyse und Parsing Lexikalische Analyse 4. Lexikalische Analyse und Parsing lex Beispiel 4.2. Eine reguläre Definition für Bezeichner im Stil von C: letter digit identifier → → → | A | B | ··· | Z | a | ··· | z 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 letter ( letter | digit )* Definition 4.2. Es sei Σ ein Alphabet und r ein regulärer Ausdruck über Σ. 1. r+ bezeichne den regulären Ausdruck rr∗. Das Unix-Programm lex ist ein Werkzeug für die lexikalische Analyse: • lex akzeptiert eine Menge von Mustern, die erweiterte reguläre Ausdrücke und Definitionen darstellen. • Hieraus produziert lex ein durch Tabellen gesteuertes C-Programm, das in der Lage ist, Folgen von Zeichen zu erkennen, die auf die Muster passen. 2. r? bezeichne den regulären Ausdruck |r. 3. Wir führen eine Notation für Zeichenklassen ein: Für c1, . . . , cn ∈ Σ bezeichne [c1 . . . cn] den regulären Ausdruck c1| · · · |cn. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing Lexikalische Analyse 180 Lexikalische Analyse Für c1 ≤ cn bezeichne [c1 − cn] den regulären Ausdruck c1| · · · |cn. • Zusätzlich können Aktionen angegeben werden, die bei Erkennung eines Musters ausgeführt werden sollen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 182 4. Lexikalische Analyse und Parsing Lexikalische Analyse Benutzung von lex: Diese beiden Arten der Bezeichnung dürfen innerhalb von [. . .] kombiniert werden. regulaere Definition lex−Programm lex.yy.c lex.l Beispiel 4.3. Mit den engeführten Bezeichnungen können Bezeichner im Stil von C durch folgenden regulären Ausdruck beschrieben werden: lex.yy.c [ A-Za-z][ A-Za-z0-9]∗ Text Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 181 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 C−Compiler a.out a.out Folge von Symbolen 183 4. Lexikalische Analyse und Parsing Lexikalische Analyse 4. Lexikalische Analyse und Parsing lex-Programme Definition von regulären Ausdr ücken mit lex: • Ziffern Buchstaben und manche Sonderzeichen repräsentieren sich selbst. Ein lex-Programm besteht aus drei Teilen: • Es stehen die üblichen Operationen zur Definition von regulären Ausdrücken zur Verfügung. Deklarationen %% Übersetzungsregeln %% Hilfsprozeduren • Die Operatoren +, ? und [ ] stehen gemäß Definition 4.2 zur Verfügung. Deklarationsteil: • Ein Punkt steht für ein beliebiges Zeichen, mit Ausnahme des Zeilentrenners. • ˆ am Anfang eines Musters repräsentiert den Anfang einer Eingabezeile. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing Lexikalische Analyse 184 Lexikalische Analyse • Es gibt eine Negation für Zeichenklassen: [ˆs] bezeichnet alle Zeichen, die nicht zu der durch s spezifizierten Zeichenklasse gehören. • $ am Ende eines Musters repräsentiert das Ende einer Eingabezeile. • \ zitiert ein einzelnes, unmittelbar folgendes Sonderzeichen. Backspace (\b), Zeilentrenner (\n) und Tabulatorzeichen (\t) werden wie in C bzw. Java repräsentiert. • Einen String bestehend aus beliebigen Zeichen kann man in DoppelAnführungszeichen angeben. Dann haben Sonderzeichen keine spezielle Bedeutung. • Der Deklarationsteil ist optional. Er enthält Optionen, Deklarationen von Variablen, symbolischen Konstanten und regulären Definitionen. • Text der zwischen den speziellen Klammern %{ und %} wird unverändert in die Ausgabe von lex übernommen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 186 Lexikalische Analyse Hilfsprozeduren: • Die Hilfsprozeduren sind ebenfalls optional. Dort kann CProgrammtext stehen, der unverändert in die Ausgabe von lex übernommen wird. • Üblicherweise stehen dort lokale Funktionen, die im zweiten Teil verwendet werden. Übersetzungsregeln: • Hier hat man eine Tabelle von Muster und Aktionen. • Für die Verwendung von regulären Definitionen benutzt man { }. • Jedes Muster ist ein regulärer Ausdruck. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 185 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 187 4. Lexikalische Analyse und Parsing Lexikalische Analyse 4. Lexikalische Analyse und Parsing Lexikalische Analyse • Jede Aktion ist eine einzelne C-Anweisung oder ein Folge von CAnweisungen eingeschlossen in { }. Innerhalb der Aktionen stehen die folgenden wichtigen Variabeln zur Verfügung: • Statt einer C-Anweisung kann auch | angegeben werden. In diesem Fall wird für das Muster die gleiche Aktion wie für das folgende Muster ausgeführt. • extern char yytext[]; • Alle Zeichen, die nicht in auf eines der Muster passen, werden in die Ausgabe kopiert. • extern int yyleng; Aus der Tabelle konstruiert lex eine Funktion yylex(), die in der Datei lex.yy.c abgelegt wird. • extern int yylineno; Die Zeichenfolge, die auf das erkannte Muster paßt. Länge von yytext. Nummer der aktuellen Eingabezeile. Übersetzt man diese C-Datei und bindet die lex-Bibliothek hinzu, hat man einen funktionierenden Scanner. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 188 Lexikalische Analyse Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 190 4. Lexikalische Analyse und Parsing Lexikalische Analyse Beispiel 4.4. Entfernen aller Großbuchstaben: Beispiel 4.5. Numerierung von Zeilen: %% [A-Z]+ %% %% ˆ.*\n %% ; Beispiel 4.6. Ausgabe aller Bezeichner mit Zeilennummer: Mit $ lex removeuc.l $ cc lex.yy.c -lfl erhält man ein Programm, daß von der Standardeingabe liest und dabei alle Großbuchstaben entfernt. Die Option -lfl unter Linux sorgt dafür, daß die flex-Bibliothek eingebunden wird. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 printf( "%4d\t%s", yylineno-1, yytext ); 189 letter [_A-Za-z] digit [0-9] letterOrDigit [_A-Za-z0-9] whitespace [ \t\n] other . %% {letter}{letterOrDigit}* {whitespace} {other} %% Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 printf( "%d: %s\n", yylineno, yytext ); ; ; 191 4. Lexikalische Analyse und Parsing Lexikalische Analyse Mehrdeutigkeiten: 4. Lexikalische Analyse und Parsing Lexikalische Analyse Beispiel 4.7. Zeichen, Wörter und Zeilen einer Datei zählen: • An dem letzten Beispiel sieht man, daß die angegebenen Muster mehrdeutig sein können. • lex verwendet zwei Regeln, um solche Mehrdeutigkeiten aufzulösen: – lex benutzt immer das Muster, das die längstmögliche Folge von Eingabezeichen repräsentiert. – Repräsentieren dabei immer noch zwei Muster die gleiche Eingabe, so wird das erste Muster innerhalb der Übersetzungsregeln verwendet. %{ int nchar, nword, nline; %} %% \n { ++nchar; ++nline; } [ˆ \t\n]+ { ++nword; nchar += yyleng; } . { ++nchar; } %% int main() { yylex(); printf( "%d %d %d\n", nchar, nword, nline ); } • Diese Regeln setzt man gezielt ein. Man gibt die spezifischeren Muster, die eine Teilmenge eines allgemeineren Musters beschreiben, zuerst an. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 192 Lexikalische Analyse • Beispiel: %% "int" [_a-zA-Z][_a-zA-Z0-9]+ %% 4. Lexikalische Analyse und Parsing 194 Lexikalische Analyse Beispiel 4.8. Lexikalische Analyse für einen Tischrechner: printf( "Schluesselwort" ); printf( "Bezeichner" ); Durch die von lex angewendeten Regeln ist sichergestellt, daß int als Schlüsselwort erkannt wird und nicht als Bezeichner. Das nächste Beispiel verdeutlicht den Gebrauch von %{ %} im Deklarationsteil und die Verwendung von Hilfsprozeduren. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 193 %{ extern double atof(); %} digits ([0-9]+) point "." sign [+-]? exponent ([eE]{sign}{digits}) %% {digits}({pt}{digits}?)?{exponent}? | {digits}?{pt}{digits}{exponent}? { printf( "%lf\n", atof( yytext ) ); } [ \t]+ ; \n | . ; %% Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 195 4. Lexikalische Analyse und Parsing Lexikalische Analyse Erkennung von Schlüsselwörtern • Die reservierten Worte einer Programmiersprache könnte man leicht als einzelne Muster definieren. 4. Lexikalische Analyse und Parsing static void classifyIdentifier() { char ** low; char ** high; char ** mid; int rc; low = rwlist; high = rwlist + sizeof( rwlist ) / sizeof( rwlist[0] ) - 1; while ( low <= high ) { mid = low + (high-low) / 2; if ( (rc=strcmp( *mid, yytext )) == 0 ) { printf( "%s ist Schluesselwort\n", yytext ); return; } • Leider vergrößert eine lange Liste solcher Muster, die sich selbst darstellen, das von lex generierte Programm deutlich. • Es ist effizienter, für reservierte Worte und Bezeichner nur ein einzelnes Muster zur Verfügung zu stellen. • In einer nachgelagerten Funktion untersucht man dann einen Bezeichner daraufhin, ob er ein reserviertes Wort darstellt. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 196 4. Lexikalische Analyse und Parsing Lexikalische Analyse Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing %{ static void classifyIdentifier(); %} Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 198 Lexikalische Analyse else if ( rc < 0 ) low = mid + 1; else high = mid - 1; Beispiel 4.9. Erkennung von Bezeichnern und Schlüsselwörtern: letter letterOrDigit %% {letter}{letterOrDigit}* %% static char *rwlist[] = { "break", "continue", ... "while" }; Lexikalische Analyse } printf( "%s ist Bezeichner\n", yytext ); [_a-zA-Z] [_a-zA-Z0-9] } classifyIdentifier(); 197 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 199 4. Lexikalische Analyse und Parsing Lexikalische Analyse Generierung eines Scanners 4. Lexikalische Analyse und Parsing 1. q00 Lexikalische Analyse 0 := eps({q0}); Q := {q00 }; δ := ∅; 2. Falls alle Zustände in Q0 markiert sind: STOP. Andernfalls gehe zu 3. • Grundlage der Generierung eines Scanners ist die Konstruktion eines endlichen Automaten zu einem regulären Ausdruck (siehe Kapitel 3). • Zunächst werden die NEAs zu den regulären Ausdrücken bzw. zu den regulären Definitionen erzeugt. 3. Sei S ein nichtmarkierter Zustand aus Q0. Markiere S. Für alle a ∈ Σ: • Die einzelnen NEAs werden zu einem Automaten zusammengefügt. • Sei T := eps({p|(q, a, p) ∈ ∆, q ∈ S}) • Falls T 6∈ Q0 dann nehme T in Q0 auf. • Erweitere δ um (S, a, T ). • Dieser NEA wird in einen DEA (ohne -Übergänge) transformiert. Gehe zu 2. • Der so entstandene DEA wird minimiert. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 200 Lexikalische Analyse Umwandlung eines NEA in einen DEA Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 202 4. Lexikalische Analyse und Parsing Lexikalische Analyse Beispiel 4.10. NEA zum regulären Ausdruck a(a|b)∗: a 0 a 1 ε 2 0 Sei M = (Q, Σ, ∆, q0, F ) ein NEA. Der zu M gehörende DEA M = (Q0, Σ, δ, q00 , F 0) ist gegeben durch: Q0 = P(Q) q00 0 F b 3 ε 4 ε ε q00 = 00 = {0}; Q0 = {00} = eps({q0}) = {S ⊆ Q|S ∩ F 6= ∅} δ(S, a) = eps({p|(q, a, p) ∈ ∆, q ∈ S}) für a ∈ Σ ausgw. Zustand 00 10 20 30 neue Zustände 10 = {1, 2, 4}, 30 = ∅ 20 = {2, 3, 4} Bemerkung: Tätsächlich tritt i.d.R. nur eine kleine Teilmenge von P(Q) als Zustand im DEA auf. schrittweise Konstruktion: Tafel ✎. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 201 neues Q0 {00, 10, 30} {00, 10, 20, 30} {00, 10, 20, 30} {00, 10, 20, 30} ∪δ {(00, a, 10), (00, b, 30)} {(10, a, 20), (10, b, 20)} {(20, a, 20), (20, b, 20)} {(30, a, 30), (30, b, 30)} 203 4. Lexikalische Analyse und Parsing Lexikalische Analyse Zugehöriger DEA: Lexikalische Analyse der Zeichen als Index angelegt, dessen Inhalt die Zeichenklassen sind. a, b {1,2,4} 4. Lexikalische Analyse und Parsing a 1’ • Ein Problem tritt auf, wenn zwei Klassen A und B nicht disjunkt sind. Dann ersetzt ein Scanner-Generator diese durch drei Klassen A \ B, B \ A und A ∩ B. 2’ a b {2,3,4} 0’ {0} In den Automaten gibt es dann überall dort, wo es einen Übergang unter A bzw. B gäbe, jeweils zwei Übergänge unter A \ B und A ∩ B bzw. B \ A und A ∩ B. b {} 3’ a, b Der Zustand 30 ist der Fehlerzustand. Er ist der Nachfolgezustand eines Zustands q unter a, wenn es keinen Übergang unter a aus q heraus gibt. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 204 Lexikalische Analyse Zeichenklassen: • Diese Aufteilung findet solange statt, bis jedes Zeichen zu genau einer Zeichenklasse gehört. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing Lexikalische Analyse Gegeben sei eine Folge von regulären Definitionen: • Zeichenklassen werden genutzt, um die entstehenden Automaten zu verkleinern. letter digit 206 d1 → r 1 .. dn → r n [a-z] [0-9] Sei Mi = (Qi, Σ, ∆i, q0,i, Fi) der NEA zu di. Man erhält den NEA für die gesamte Folge der regulären Definitionen, indem man: Statt 36 Übergängen benötigt man so nur zwei Übergänge. • Sämtliche Symbole werden auf diese Weise (implizit) einer Zeichenklassen zugeordnet. Für Zeichen, die nicht explizit in einer Klasse vorkommen, wird implizit eine eigene Klasse definiert. • einen neuen Startzustand q0 generiert und • -Übergänge von q0 zu jedem q0,i legt. • Für die effiziente Implementierung wird ein Feld mit dem ASCII-Code Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 205 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 207 4. Lexikalische Analyse und Parsing Lexikalische Analyse 4. Lexikalische Analyse und Parsing Konstruktion eines Parsers • Für yacc wird die Grammatik gemäß BNF angegeben. Eine Grammatik besteht aus einer Folge von Regeln. Veranschaulichung: q0,1 • Eine Regel besteht aus einer linken Seite und einer rechten Seite, die durch einen Doppelpunkt gestrennt sind. ε q0 ε • Die linke Seite besteht aus einem eindeutigen Grammatikbegriff (non-terminal Symbol) q0,2 ε • Die rechte Seite besteht aus einer Folge von Formulierungen, die durch | voneinander getrennt sind. q0,3 • Da Grammatikbegriffe und (Terminal-)Symbole gleich aussehen, muß man die Symbole vor der eigentlichen Grammatik vereinbaren. • %% trennt diese beiden Teile. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 208 Konstruktion eines Parsers Konstruktion eines Parsers mit yacc Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 210 Konstruktion eines Parsers Beispiel 4.11. Erstes Beispiel einer yacc-Grammatik: %token NUMBER • Ein Compiler muß entscheiden, ob eine Folge von Eingabesymbolen ein Satz in einer Sprache ist. Eine Grammatik beschreibt eine Sprache. • Können wir eine Grammatik direkt für die Erkennung einer Sprache verwenden? • Prinzipiell sollte dies möglich sein. Kontextfreie Sprachen können mit Kellerautomaten erkannt werden. • Das Programm yacc ermöglicht es uns, einen Parser für eine Sprache auf der Basis ihrer Grammatik zu generieren. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 209 %% expression : expression ’+’ NUMBER | NUMBER Liegt dieser Text in der Datei first.y so wird mit dem Befehl $ yacc first.y eine Datei y.tab.c erzeugt, die die wesentlichen Teile des Parsers enthält. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 211 4. Lexikalische Analyse und Parsing Konstruktion eines Parsers • yacc prüft die definierte Grammatik und konstruiert einen Parser zur Erkennung der Sprache. • Der Parser ist ein Kellerautomat, der besteht aus: – einem großen Stack, auf dem die aktuellen Zustände abgelegt werden, – einer Übergangsmatrix, mit der für jede mögliche Kombination von aktuellem Zustand und nächstem Eingabesymbol ein neuer Zustand bestimmt wird, – einer Tabelle von Aktionen, die vom Benutzer definiert sind und in bestimmten Situationen der Erkennung ausgeführt werden und – einem Interpreter, der für die Ausführung des Kellerautomaten sorgt. • Das ganze ist verpackt als Funktion yyparse(), die sich der Funktion yylex() zur lexikalischen Analyse bedient. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 212 Konstruktion eines Parsers 4. Lexikalische Analyse und Parsing Konstruktion eines Parsers Beispiel 4.12. lex-Programm first.l zu first.y: %{ #include "y.tab.h" %} whitespace other %% [0-9]+ {whitespace}+ {other} %% [ \t\n] . return( NUMBER ); ; return( yytext[0] ); Weiterhin brauchen wir noch eine main()-Funktion, die yyparse() aufruft. int main() { yyparse(); } Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 214 Konstruktion eines Parsers Als letztes wird noch eine Funktion yyerror benötigt. Diese Funktion ruft der Parser auf, wenn er einen Syntaxfehler erkennt. Putting it all Together #include <stdio.h> Die Repräsentation von Symbolen muß zwischen yylex() und yyparse() abgestimmt werden. yacc hilft hierbei. Das Kommando yyerror( char * errmsg ) { fprintf( stderr, "%s\n", errmsg ); } $ yacc -d first.y Diese Funktionen seien in einer Datei first.c. Die Befehle sorgt dafür, daß eine Datei y.tab.h mit C-Präprozessor-Anweisungen erzeugt wird. $ lex first.l $ yacc -d first.y $ cc first.c lex.yy.c y.tab.c -lfl Diese Datei sollte in lex importiert werden (#include-Anweisung). erzeugen dann in a.out einen Parser. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 213 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 215 4. Lexikalische Analyse und Parsing Konstruktion eines Parsers 4. Lexikalische Analyse und Parsing Konstruktion eines Parsers – Der Wert des an i-ter Stelle stehenden Grammatikbegriff einer Formulierung wird mit $i bezeichnet. – Die Übergabe eines Wertes von lex nach yacc geschieht durch Zuweisung an die Variable yylval. $ a.out 4711 + 0815 ˆD $ Da die Eingabe korrekt in bezug auf die definierte Grammatik ist, wird keine Ausgabe erzeugt. Andernfalls gibt es eine kurze Fehlermeldung: syntax error. Beispiel 4.13. Konstruktion eines simplen Addierers: %token NUMBER %% calculation : expression { printf( "%d\n", $1 ); } expression : expression ’+’ NUMBER { $$ = $1 + $3; } | NUMBER Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 216 Konstruktion eines Parsers 4. Lexikalische Analyse und Parsing 218 Konstruktion eines Parsers Das zugehörige lex-Programm: Ausführen von Aktionen • Mit den bisherigen Techniken kann nur überprüft werden, ob ein Text syntaktisch korrekt aufgebaut ist. • Möchte man den Text verarbeiten, ist es notwendig, mit den Grammatikbegriffen Aktionen zu verbinden. • Hierzu bietet yacc die folgenden Möglichkeiten: – Zu jeder Formulierung kann ein Block von C-Anweisungen angegeben werden. – Mit jedem Grammatikbegriff ist ein Wert verbunden. – Der Wert, der mit dem Grammatikbegriff der linken Regelseite verbunden ist, trägt die Bezeichnung $$. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 217 %{ #include "y.tab.h" extern int yylval %} whitespace [ \t\n] other . %% [0-9]+ { yylval = atoi( yytext ); return( NUMBER ); } {whitespace}+ ; {other} return( yytext[0] ); Aufruf: $ a.out 4711 + 0815 ˆD 5526 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 219 4. Lexikalische Analyse und Parsing Konstruktion eines Parsers pt "." sign [+-]? exponent ([eE]{sign}{digits}) %% {digits}({pt}{digits}?)?{exponent}? {digits}?{pt}{digits}{exponent}? Beispiel 4.14. Definitionen für einen Tischrechner: %{ #define YYSTYPE double %} %token NUMBER %left ’+’ ’-’ %left ’*’ ’/’ %% line : /* leer */ | line expression ’\n’ { printf( "%lf\n", $2 ); } [ \t]+ \n . %% 4. Lexikalische Analyse und Parsing Konstruktion eines Parsers | { yylval = atof( yytext ); return( NUMBER ); } ; | return yytext[0]; Bemerkungen: • Mittels left bzw. right wird die Assoziativität von Operatoren vereinbart. expression : expression ’+’ expression { $$ = $1 + $3; } Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing • Darüberhinaus auch die Priorität. Die Operatoren haben gemäß der 220 Konstruktion eines Parsers Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 4. Lexikalische Analyse und Parsing 222 Konstruktion eines Parsers Reihenfolge aufsteigende Priorität. | expression ’-’ expression { $$ = $1 - $3; } | expression ’*’ expression { $$ = $1 * $3; } | expression ’/’ expression { $$ = $1 / $3; } | ’(’ expression ’)’ { $$ = $2; } | NUMBER • yylval und der Wert-Stack werden im Parser mit dem Typ YYSTYPE vereinbart. Hier ist int voreingestellt. Mittels der angegebenen Definition kann dies umgesetzt werden. lex-Programm: %{ #include "y.tab.h" extern double atof(); extern double yylval; %} digits ([0-9]+) Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 221 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 223 5. XML und Analyse von XML-Dokumenten Einf ¨uhrung in XML 5 XML und Analyse von XML-Dokumenten 5. XML und Analyse von XML-Dokumenten Einf ¨uhrung in XML Was ist XML? • XML ist eine Sprache zur Repräsentation von Dokumenten. Erläuterungen zu XML vom W3C: • Die Extensible Markup Language (XML) ist ein einfaches und sehr flexibles, von SGML abgeleitetes Textformat. • SGML := Standard Generalized Markup Language • XML wurde entworfen, um die hohen Anforderungen an das elektronische Publizieren abzudecken und • wird darüberhinaus eine wichtige Rolle für den Austausch unterschiedlichster Daten über das Web spielen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 224 Einf ¨uhrung in XML • Durch XML wird gemäß SGML die Auszeichnung von Dokumenten (markup) formalisiert und von System- und Verarbeitungsabhängigkeiten gelöst. • Durch XML wird eine Syntax bereitgestellt, mit der die Struktur von Dokumenten eines Typs unabhängig von der weiteren Verwendung und Darstellung beschrieben werden kann. • Oft wird XML als Metasprache bezeichnet, da über XML Auszeichnungssprachen definiert werden können (erweiterbare Auszeichnungssprache). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 226 Einf ¨uhrung in XML Auszeichnungssprache Was soll durch XML ermöglicht werden? Gemäß W3C: • Medienunabhängiges elektronisches Publizieren von Informationen in mehreren Sprachen • Bei einer Auszeichnungssprache werden die Inhalte eines Datenstroms durch Auszeichnungen (tags) strukturiert. • Definition von plattformunabhängigen Protokollen zum Datenaustausch (speziell für den elektronischen Handel) • Ausschließlich die Auszeichnungen dienen zur Strukturierung des Inhalts. • Automatische Verarbeitung übertragener Daten durch Software • Die Auszeichnungen sind so gehalten, daß sie selbst als reiner Text innerhalb des Inhalts eines Dokuments zu identifizieren sind. • Datenverarbeitung mit preisgünstiger Software • Benutzerdefinierte Präsentation von Informationen • Durch solche Auszeichnungen werden Teile des Inhalts benannt. Diese Inhalte heißen Elemente. • Auffinden von Informationen durch Metadaten (Informationen über Informationen) • Die Länge der Auszeichnungen ist variabel und kann mit Informationen über den ausgezeichneten Inhalt angereichert werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 225 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 227 5. XML und Analyse von XML-Dokumenten Einf ¨uhrung in XML Dokument und Dokumenttyp Ein XML-Dokument ist eine Instanz eines XML-Dokumenttyps, der wiederum durch die Dokumentenrepräsentationssprache XML beschrieben wird. Einf ¨uhrung in XML <ProdName>Web-Visitenkarte</ProdName> <UnitPrice>1.00</UnitPrice> </Item> </Items> </Order> Order Dokumenten− repräsentations− sprache XML SGML 5. XML und Analyse von XML-Dokumenten OrderHeader Items Dokumenttyp HTML XHTML MeinTyp OrderID Item OrderDate Customer HTML− Dokument XHTML− Dokument MeinTyp Dokument Dokumentinstanz CustName Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 228 Einf ¨uhrung in XML Beispiel 5.1. Ein XML-Dokument zur Repräsentation einer Bestellung als erstes CustEmailAdress ProdName UnitPrice Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 230 Einf ¨uhrung in XML Wohlgeformtheit einfaches Beispiel: <?xml version="1.0"?> <!DOCTYPE Order SYSTEM "http://some-provider.de/order.dtd"> <Order> <OrderHeader> <OrderID>4711</OrderID> <OrderDate>2000-11-11</OrderDate> <Customer> <CustName>Dr. Peter Becker</CustName> <CustEmailAdress> [email protected] </CustEmailAdress> </Customer> </OrderHeader> <Items> <Item quantity="1" deliveryDate="2001-01-02"> Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Ein XML-Dokument ist wohlgeformt, wenn es: • Die syntaktischen Regeln von XML erfüllt, • alle Elemente ordnungsgemäß verschachtelt sind und • alle referenzierten Entities geeignet deklariert sind. ☞ Für wohlgeformte Dokumente kann der Strukturbaum erstellt werden, ohne daß Kenntnisse über den Dokumenttyp erforderlich sind. 229 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 231 5. XML und Analyse von XML-Dokumenten Einf ¨uhrung in XML Dokumenttypen 5. XML und Analyse von XML-Dokumenten Einf ¨uhrung in XML Beispiel 5.2. Eine DTD für das Dokument von Beispiel 5.1: • Für die Verarbeitung von Daten ist das Vorhandensein einer Schemadefinition elementar. • Auch wenn ein XML-Dokument wohlgeformt ist, so ist eine sinnvolle Weiterverarbeitung erst durch die Angabe eines zugehörigen Dokumenttyps möglich. <!DOCTYPE Order [ <!ELEMENT Order (OrderHeader, Items) > <!ELEMENT OrderHeader (OrderID, OrderDate, Customer) > <!ELEMENT Customer (Custname, CustEmailAdress?) > <!ELEMENT Items (Item+) > <!ELEMENT Item (ProdName, UnitPrice) > <!ATTLIST Item quantity CDATA #REQUIRED deliveryDate CDATA #IMPLIED > <!ELEMENT ProdName (#PCDATA) > . . . ]> • Es muß im voraus bekannt sein, welche Elemente in einem Dokument vorkommen können, um auf diesen sinnvolle Operationen ausführen zu können. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 232 Einf ¨uhrung in XML Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 234 Einf ¨uhrung in XML Dokumenttypdeklaration • Über die Deklaration eines Elements wird sein Name und sein Inhaltsmodell beschrieben. • Ein XML-Dokument kann eine Dokumenttypdeklaration (document type declaration) enthalten. • Soll das Element keine weiteren Unterelemente haben, so ist das Inhaltsmodell EMPTY (leer) oder PCDATA (nur Zeichen enthaltend). • In solch einer Dokumenttypdeklaration werden die zur Verfügung stehenden Auszeichnungen direkt angegeben oder • Ansonsten wird eine Strukturierung von Unterelementen angegeben. Hierfür stehen die folgenden Strukturierungsmöglichkeiten zur Verfügung: • es wird auf eine Auszeichnungsdeklaration verwiesen. • In der Auszeichnungsdeklaration wird eine Grammatik angegeben, der das aktuelle Dokument folgen soll. • Diese Grammatik wird als Dokumenttypdefinition (document type definition, DTD) bezeichnet. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 233 – Sequenz: Unterelemente werden durch Komma getrennt angegeben – Auswahl: Unterelemente werden durch | getrennt angegeben – Wiederholung: Ein + hinter einem Unterelement gibt an, daß dieses beliebig oft auftreten kann, jedoch mindestens einmal auftreten muß. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 235 5. XML und Analyse von XML-Dokumenten Einf ¨uhrung in XML – Wiederholung: Ein * hinter einem Unterelement gibt an, daß dies beliebig oft eventuell auch gar nicht auftreten kann. – Option: Ein ? hinter einem Unterelement gibt an, daß dieses einmal oder gar nicht auftreten kann. • PCDATA steht für Parsed Character Data. Dies sind Zeichenfolgen, die spitze Klammern, Apostroph, Anführungszeichen und & nicht enthalten. • Diese Zeichen sind Bestandteil der Auszeichnung. Sie müssen über sogenannte Entity-Referenzen aufgelöst werden. Innerhalb solcher Zeichenfolgen werden Entity-Referenzen ersetzt. • CDATA steht für Character Data. Hier ist alles erlaubt, es findet keine Auflösung der Zeichen statt. 5. XML und Analyse von XML-Dokumenten Einf ¨uhrung in XML In einer DTD können interne und externe Entity-Referenzen deklariert werden. Beispiel 5.3. <!ENTITY vorlesungstitel "Textalgorithmen: unverstaendlich und praxisfern"> In einem XML-Dokument kann nun eine Referenz wie folgt verwendet werden: <titel>&vorlesungstitel;</titel> Beispiel 5.4. Durch die Deklaration von <!ENTITY ueberdendozent SYSTEM "http://www.inf.fh-bonn-rhein-sieg.de/becker.xml"> wird eine Referenz auf ein externes XML-Dokument erzeugt. Verwendet Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 236 Einf ¨uhrung in XML Entity-Referenzen Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 238 Einf ¨uhrung in XML • Entity-Referenzen sind Platzhalter für Ersetzungen. man nun &ueberdendozent;, so wird an dieser Stelle das komplette XML-Dokument eingesetzt. • Eine Entity-Referenz kann Zeichendaten beschreiben oder eine komplette XML-Instanz. • Durch externe Entity-Referenzen werden XML-Dokumente für andere XML-Dokumente wiederverwendbar. • Ein Entity ist irgendeine Einheit von wohlgeformten XML, auf die eine Entity-Referenz entweder direkt oder über eine URL verweist. • Bei der Einfügung findet wiederum eine Ersetzung von EntityReferenzen statt. • Entity-Referenzen werden in der DTD deklariert. • Ein XML-Dokument muß also nicht physikalisch aus einer einzelnen Datei bestehen. • In PCDATA haben sie die Form: &Name; • Einige Entity-Referenzen kennt der Parser implizit. Dies sind die Entity-Referenzen für die Darstellung der in PCDATA nicht erlaubten Zeichen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 237 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 239 5. XML und Analyse von XML-Dokumenten Einf ¨uhrung in XML 5. XML und Analyse von XML-Dokumenten Einf ¨uhrung in XML Dokumentzentrierte XML-Dokumente Gültigkeit Ein wohlgeformtes XML-Dokument heißt gültig, wenn es: • inhomogen strukturiert • die durch die DTD definierten Beschränkungen erfüllt. • Reihenfolge der Elemente ist oft wichtig • viel “mixed content” • Informationen auf allen Ebenen • Volltextsuche ist unbedingt notwendig Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 240 Einf ¨uhrung in XML Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 242 5. XML und Analyse von XML-Dokumenten Datenzentrierte XML-Dokumente Analyse von XML-Dokumenten SAX • Repräsentation von sehr stark strukturierten Informationen, Daten im herkömmlichen Sinn (z.B. in relationalen DB) • Reihenfolge ist oft irrelevant • Simple API for XML (SAX) ist ein de-facto Standard für ein ereignisbasiertes Parsen von XML-Dokumenten. • SAX definiert eine Schnittstelle, die es erlaubt, die Informationen eines XML-Dokumentes in linearer Reihenfolge zu durchlaufen, ohne eine Baumstruktur aufzubauen. • sind einheitlich und meist homogen strukturiert • haben Datentypen • Es existieren Implementierungen für die meisten gängigen Programmiersprachen (Java, Perl, Python, C++). • Informationen liegen in den Blättern • selten “mixed content” Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 241 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 243 5. XML und Analyse von XML-Dokumenten Analyse von XML-Dokumenten Bei einer ereignisbasierten API gibt es zwei wichtige Programmkomponenten: 1. Einen Treiber der Ereignisse erzeugt. Hier ist dies der XML-Parser. 2. Handler, die diese Ereignisse verarbeiten. Hier sind dies Klassen, die bestimmte Schnittstellen implementieren. • Es muß nicht das gesamte Dokument gelesen werden, bevor es bearbeitet werden kann. Die Baumstruktur des Dokuments wird nicht aufgebaut. • Stattdessen werden beim Parsen Ereignisse erzeugt. Diese können Aktionen auslösen. 5. XML und Analyse von XML-Dokumenten Analyse von XML-Dokumenten Die Ereignisse können grob in die folgenden Arten eingeteilt werden: Ereignismodell von SAX Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 244 Analyse von XML-Dokumenten Beispiel 5.5. Für das XML-Dokument • Ereignisse durch den Inhalt eines XML-Dokument • Ereignisse durch Validierung • Ereignisse durch Fehler Um auf Ereignisse reagieren zu können, müssen sogenannte Handler implementiert werden. Wird zu einem Ereignis kein passender Handler gefunden, so bleibt dieses Ereignis unbehandelt. Zu den meisten Ereignissen werden vom Parser Parameter übergeben. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 246 5. XML und Analyse von XML-Dokumenten Analyse von XML-Dokumenten <<interface>> ContentHandler <?xml version="1.0"?> <rowset> <row>SAX Test</row> </rowset> <<interface>> ErrorHandler <<interface>> DTDHandler <<interface>> EntityHandler :MyContentHandler werden die folgenden Ereignisse erzeugt: 4.1:setDocumentLocator() StartDocument() StartElement( "rowset" ) StartElement( "row" ) Characters( "SAX Test" ) EndElement( "row" ) EndElement( "rowset" ) EndDocument() :MyErrorHandler 4.2:startDocument() :MyDTDHandler 4.3:startElement() 4.4:characters() :MyEntityHandler 4.5:endElement() 4.n: endDocument() <<interface>> 2:new() :XMLReader main 3:setContentHandler() 1:createXMLReader() 4:parse() :SAXParser Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 245 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 :XMLReaderFactory 1.1:new() 247 5. XML und Analyse von XML-Dokumenten Analyse von XML-Dokumenten Schnittstellen zum Parser: • Der XML Reader dient dazu, den XML-Parser zu konfigurieren, die Event Handler zu registrieren sowie das Parsen zu starten. • Durch die Implementierung des Content Handlers kann auf Ereignisse reagiert werden, die durch den Inhalt eines XML-Dokuments entstehen. 5. XML und Analyse von XML-Dokumenten Analyse von XML-Dokumenten parser.parse( uri ); } catch ( IOException e ) { System.out.println( "Error reading URI: " + e.getMessage() ) ; } catch ( SAXException e ) { System.out.println( "Error parsing URI: " + e.getMessage() ) ; } } • Der Error Handler unterscheidet zwei Arten von Fehlern: – Error: Fehler, die beim Parsen übersprungen werden können – Fatal Errror: Fehler, die zum Abbruch des Parsens führen. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten 248 Analyse von XML-Dokumenten Beispiel 5.6. Parsing von XML-Dokumenten mit SAX: 5. XML und Analyse von XML-Dokumenten 250 Analyse von XML-Dokumenten DOM: Bearbeitung von XML-Dokumenten public void parserDemoSAX( String uri ) { System.out.println( "Parsing XML File: " + uri + "\n\n" ); • DOM ist ein Standard (W3C), SAX ist public-domain Software. contentHandler = new MyContentHandler(); errorHandler = new MyErrorHandler(); • DOM zielt ab auf die Bearbeitung eines Dokuments, SAX auf das Parsing eines XML-Dokuments. try { • DOM Level 1: Generische Navigation und Bearbeitung eines Dokuments XMLReader parser = XMLReaderFactory.createXMLReader( PropertiesReader().getInstance() .getProperty( "parserClass" ) ); • DOM spezifiziert Interfaces für die entsprechenden Funktionalitäten und verschiedene Sprachen: z.B. Java, Javascript und CORBA. parser.setContentHandler( contentHandler ); parser.setErrorHandler( errorHandler ); Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 249 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 251 5. XML und Analyse von XML-Dokumenten Analyse von XML-Dokumenten Navigation mit dem Document Object Model "Error parsing URI: " + e.getMessage() ) ; } public void printNode( Node node, String ident ) { System.out.println( ident + node.getNodeName() ); System.out.println( ident + node.getNodeValue() ); getParentNode() getPreviousSibling() OrderHeader Items Node child = node.getFirstChild(); while ( child != 0 ) { printNode( child, ident + " " ); child = child.getNextSibling(); } } getNextSibling() OrderID Item OrderDate Customer CustName CustEmailAdress ProdName UnitPrice Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 5. XML und Analyse von XML-Dokumenten Analyse von XML-Dokumenten } Order getFirstChild() 5. XML und Analyse von XML-Dokumenten 252 Analyse von XML-Dokumenten Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 254 6. Textkompression Allgemeines zur Textkompression 6 Textkompression DOM: Beispiel in Java • Das Ziel der Datenkompression ist es, eine gegebene Information (Datenquelle) auf eine kompaktere Weise zu repräsentieren. public void parserDemoDOM( String uri ) { System.out.println( "Parsing XML File: " + uri + "\n\n" ); • Dies geschieht, indem Strukturen bzw. Regelmäßigkeiten in der Datenquelle erkannt und ausgenutzt werden. DOMParser parser = new DOMParser(); try { • Beispiel: Morse-Alphabet Häufig vorkommende Buchstaben haben einen kürzeren MorseCode als weniger häufige. parser.parse( uri ); Document doc = parser.getDocument(); printNode( doc, "" ); e j 1 • •––– •–––– } catch ( Exception e ) { System.out.println( Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 253 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 255 6. Textkompression Allgemeines zur Textkompression Vorgehensweise bei der Kompression: Modell Datenquelle Text Allgemeines zur Textkompression Modellierungsansatz: Modell Komprimierer 6. Textkompression Dekomprimierer • Bei statistischen Verfahren wird versucht, eine Wahrscheinlichkeit für Zeichen oder Zeichenfolgen zu bestimmen. Die Kompression erfolgt, indem Zeichenfolgen mit einer höheren Wahrscheinlichkeit ein kürzerer Code zugewiesen wird. Text Kommunikationskanal • Komprimierer und Dekomprimierer verfügen über das gleiche Modell. • Bei wörterbuchbasierten Verfahren versucht man, Zeichenfolgen durch eine Referenz auf ein Wörterbuch zu ersetzen. Hierzu enthält das Wörterbuch häufig auftretende Zeichenfolgen. • Der Komprimierer erzeugt eine Codierung der Datenquelle auf der Basis des Modells. • Der Dekomprimierer nutzt das gleiche Modell, um aus der komprimierten Darstellung die Datenquelle zu rekonstruieren. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 6. Textkompression 256 Allgemeines zur Textkompression Eigenschaften von Kompressionsverfahren 6. Textkompression 258 Allgemeines zur Textkompression Anpassung des Modells bzw. der Codierung an die Datenquelle: • Bei statischen Verfahren wird stets die gleiche Codierung für die Kompression benutzt. Es findet keine Anpassung an die Datenquelle statt (Morse-Alphabet). Informationsverluste bei der Kompression/Dekompression: • Verfahren, bei denen die Datenquelle exakt aus der komprimierten Darstellung rekonstruiert werden kann, heißen verlustfrei. • Treten dagegen möglicherweise Informationsverluste auf, spricht man von nicht verlustfreien Verfahren. Solche Verfahren sind z.B. für Bilder, Videos oder Audioübertragung geeignet. • Vorteil der nicht verlustfreien Verfahren: höhere Kompressionsraten. • Für die Textkompression kommen nur verlustfreie Verfahren in Frage. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 257 • Bei semi-adaptiven Verfahren wird die Datenquelle zunächst einer Häufigkeitsanalyse unterzogen, und die Codierung wird entsprechend angepaßt. Nachteil: Die Datenquelle muß zweimal gelesen werden, und der Dekomprimierer benötigt zusätzliche Informationen für die Dekomprimierung. • Bei adaptiven Verfahren wird die Codierung für die Komprimierung und Dekomprimierung während der Komprimierung angepaßt. Die Datenquelle muß nur einmal gelesen werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 259 6. Textkompression Allgemeines zur Textkompression 6. Textkompression Allgemeines Modell der Textkompression Allgemeines zur Textkompression Wörterbuchbasierte Verfahren • Gegeben ist eine Datenquelle s als String über dem Alphabet {0, 1} (Bitstring). • Das Dictionary für die Kompression läßt sich bei wörterbuchbasierten Verfahren darstellen als • Die Ausgabe c eines Kompressionsverfahrens ist ebenfalls ein Bitstring. • Weiterhin sei ein Alphabet A gegeben. D = {(f, c)|f ∈ F, c ∈ {0, 1}∗} Hierbei ist F eine Teilmenge der Substrings von s. • Ein Kompressionsverfahren kann mit Hilfe von zwei injektiven Funktionen g und h beschrieben werden, die von A∗ nach {0, 1}∗ abbilden. • Die f ∈ F übernehmen hier die Rolle der a ∈ A. • Die Menge {(g(a), h(a))|a ∈ A} wird als Dictionary des Kompressionsverfahrens bezeichnet. • Die Datenquelle kann als eine Konkatenation von vielen f ∈ F angesehen werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 6. Textkompression 260 Allgemeines zur Textkompression • Die Funktion g gibt im Prinzip an, wie die Bits von s zu interpretieren sind. g −1 sukzessive angewendet auf s liefert einen String text ∈ A∗. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 6. Textkompression 262 Allgemeines zur Textkompression • Für ein wörterbuchbasiertes Kompressionsverfahren müssen nun die folgenden Punkte festgelegt werden: – Man finde eine geeignete Menge F von Substrings von s, – man bestimme, wie s als Konkatenation von f ∈ F dargestellt wird (Faktorisierung), und – man lege die Codierung für die f ∈ F fest. • Die Funktion h gibt die Codierung der a ∈ A an. • c entsteht durch sukzessive Anwendung von h auf text. • Beispiel: Wandlung eines (maschinenlesbaren) Textes in MorseZeichen. A ist die Menge der Buchstaben. g bildet Buchstaben in den ASCIICode ab. h definiert den Morse-Code, d.h. wie Buchstaben in MorseZeichen codiert werden. • Bei einer festen Funktion g kann der Algorithmus allein durch h beschrieben werden. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 261 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 263 6. Textkompression Allgemeines zur Textkompression 6. Textkompression Optimale Faktorisierung Lempel-Ziv-Codierung Gegeben sei ein Wörterbuch. Man bestimme nun eine Faktorisierung eines Textes, so daß die Gesamtlänge der verwendeten Codes minimal ist. f a b ba bb abb Beispiel 6.1. c 00 010 0110 0111 1 l(c) 2 3 4 4 1 Wie kann damit der Text “babb” optimal faktorisiert werden? Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 • Die bekanntesten wörterbuchbasierten Kompressionsalgorithmen basieren auf den Verfahren von A. Lempel und J. Ziv (LZ). • Dies sind adaptive Verfahren, die wie folgt vorgehen: – Die Menge F wird dynamisch in Abhängigkeit von s während des Kompressionsvorgangs bestimmt. – Für die Faktorisierung wird eine sogenannte Greedy-Strategie benutzt, d.h. man bestimmt aus dem aktuellen F jeweils das längste f , das einen Präfix von s darstellt. – Als Codierung werden Verweise (Pointer) auf den jeweiligen Dictionary-Eintrag verwendet. • Es gibt im Prinzip zwei Familien von Kompressionsalgorithmen, die auf den Verfahren LZ77 und LZ78 beruhen. 264 6. Textkompression Allgemeines zur Textkompression Allgemeines zur Textkompression Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 6. Textkompression • Repräsentation als kürzestes Wege Problem Faktorisierung und Erstellung des Dictionary bei LZ78: • Es gibt eine Kante von Knoten i zu Knoten j, wenn text[i . . . j − 1] im Dictionary auftritt. Die Länge des zugehörigen Codes bestimmt das Gewicht der Kante. • Ein kürzester Weg von Knoten 1 nach Kanoten n + 1 repräsentiert eine optimale Faktorisierung. 1 3 2 3 3 4 3 • In einem beliebigen Iterationsschritt sei s der noch zu komprimierende Rest der Datenquelle. 5 • Ausgegeben wird der Verweis auf den zu f gehörenden DictionaryEintrag und das Zeichen a. 1 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 • Zunächst besteht das Dictionary nur aus dem leeren String . Man sucht nun für die Faktorisierung im Dictionary das größte f , so daß gilt: s = f az. Hierbei ist a genau ein Zeichen und z ein beliebiger String. 4 2 Allgemeines zur Textkompression LZ78 • Für Text der Länge n werden n + 1 Knoten eingeführt. 4 266 265 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 267 6. Textkompression Allgemeines zur Textkompression • Der String f a wird neu in das Dictionary aufgenommen. 6. Textkompression Allgemeines zur Textkompression Beispiel 6.2. Es sei s = aababbabbabb. Dies führt zu der Faktorisierung: • Anschließend verfährt man mit dem Rest z der Datenquelle genauso. a ab abb abba b b# • Damit garantiert ist, daß eine Darstellung f az stets möglich ist, fügt man an s ein Zeichen # an, das in s nicht vorkommt. Nach Ablauf des Algorithmus liegt das folgende Dictionary vor: Darstellung der Verweise in LZ78: k 0 1 2 3 4 5 6 • Es werden Verweise variabler Länge benutzt, wobei die Länge von der aktuellen Größe des Dictionary abhängt. • Es sei n die Anzahl der Einträge im Dictionary. Dann gilt für die verwendete Länge l(n) der Verweise: l(1) = 1 und l(n) = dlog2(n)e für n > 1 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 6. Textkompression Algorithmus 6.1. [LZ78-Kompression] ste (f0, f1, . . . , fn−1) von Strings. 268 Allgemeines zur Textkompression Das Dictionary D sei eine Li- fk a ab abb abba b b# Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 270 6. Textkompression Allgemeines zur Textkompression Anschaulich dargestellt ergibt sich die Ausgabe 0a 1b 10b 11a 000b 101# D := {} x := s# while x 6= do fk := längstes fk ∈ F , so daß x = fk az gilt, mit a ∈ A a := Zeichen, daß auf fk in x folgt Ausgabe von k in l(|D|) Bits Ausgabe von a in Orginalcodierung (z.B. ASCII) Aufnahme von fk a als f|D| in D x := z end 0 a b 1 Veranschaulichung: Dictionary aus Beispiel 6.1 als Trie 5 b # 2 6 b 3 a 4 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 269 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 271 6. Textkompression Allgemeines zur Textkompression 6. Textkompression Allgemeines zur Textkompression Bemerkungen: Implementierungsaspekte • Es gibt eine Vielzahl von Varianten des LZ78-Algorithmus. • Entscheidend für die Effizienz von LZ78 ist die schnelle Bestimmung der fk . • Hierzu legt man das Wörterbuch in einem Trie ab. • Jeder Knoten des Tries entspricht einem Dictionary-Eintrag. • Man liest nun Zeichen für Zeichen von x und steigt, ausgehend von der Wurzel, sukzessive den Trie hinab. • Kommt man mit dem letzten gelesenen Zeichen im Trie nicht weiter, dann entspricht der aktuelle Knoten dem fk , und das letzte gelesene Zeichen ist das zusätzliche Zeichen a. Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 6. Textkompression 272 Allgemeines zur Textkompression • Zur Erweiterung des Dictionary fügt man ausgehend vom aktuellen Knoten eine mit a markierte Kante und einen neuen Knoten ein. Der neue Knoten repräsentiert den Dictionary-Eintrag fk a. • Diese Varianten unterscheiden sich z.B. in bezug auf: – – – – Die Benutzung variabler oder fixer Verweislängen, die maximale Größe des Dictionary, das Vorgehen, wenn das Dictionary voll ist und die Behandlung des einen zusätzlichen Zeichens bei der Faktorisierung. • Das U NIX-Programm compress, bekannt als LZC, basiert auf LZ78. Es ist eigentlich hervorgegangen aus LZW, wobei aber viele Aspekte von LZ78 wieder übernommen wurden, so z.B. die variablen Verweislängen (im Gegensatz zu LZW, das fixe Verweislängen verwendet). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 6. Textkompression 274 Allgemeines zur Textkompression • Laut Manual-Seite reduziert compress Textdateien auf 50 bis 60 % der Orginalgröße. • Anschließend springt man wieder an die Wurzel des Tries. • Das bekannte Kompressionsprogramm gzip basiert im Gegensatz zu compress auf dem LZ77-Verfahren. • Gesamtaufwand: O(|s|) • Bei LZ77 wird – die Länge der Dictionary-Einträge durch einen Parameter F beschränkt, und – das Dictionary bezieht sich stets nur auf die letzten N Zeichen. – Dafür enthält das Dictionary jeden Substring der letzten N − F Zeichen mit der maximalen Länge F . – Typische Werte für N und F : 10 ≤ F ≤ 20, N ≤ 8192 • In der Praxis hat sich LZ77 (gzip) als etwas besser erwiesen als LZ78 (compress). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 273 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 275 6. Textkompression Allgemeines zur Textkompression • LZ78 hat aber – die besseren theoretischen Eigenschaften und – die beste bekannte Variante von LZ78 ist besser als die beste bekannte LZ77-Variante. 6. Textkompression Allgemeines zur Textkompression • Es wird nun nach einer Position innerhalb der ersten N − F Zeichen nach einem größtmöglichen Match maximal der Länge F für den Beginn des lookahead buffers gesucht. Beispiel: Position 10, String bab • Ausgegeben wird nun: – Die Startposition innerhalb des ersten Teils – Die Länge des Match – Das erste Zeichen, das nicht auf den Match passte • Anschließend wird das Fenster um j +1 Zeichen nach rechts geschoben. • Typische Werte für N und F : 10 ≤ F ≤ 20, N ≤ 8192 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 276 6. Textkompression Allgemeines zur Textkompression • Die ersten N − F Zeichen dieses Fensters wurden bereits codiert. • Die verbleibenden F Zeichen sind der sogenannte lookahead buffer. 5 6 7 8 9 10 11 12 13 14 15 b c b a c bereits kodiert Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 Allgemeines zur Textkompression • Zu Beginn sind die ersten N − F Zeichen Leerzeichen und der lookahead buffer enthält die ertsen F Zeichen des Textes. • Über den Text wird ein Fenster der Größe N geschoben. a 6. Textkompression 278 • Für die Verweise in das Dictionary und die Längenangabe werden konstante Längen benutzt. LZ77 b Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 b a b Decodierung: • Der Decoder verwaltet ein Fenster der gleichen Größe wie der Kodierer. • Durch die Verweise wird der lookahead buffer gefüllt und das Fenster nach rechts geschoben. c lookahead buffer 277 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 279 6. Textkompression Allgemeines zur Textkompression Vergleich der Verfahren Siehe: T. C. B ELL, J. G. C LEARY und I. H. W ITTEN: Text Compression. Durchschnittliche Anzahl an Bits der komprimierten Datei pro Byte der Datenquelle: Verfahren Huffman LZ77 LZB LZ78 LZC LZFG Bits pro Byte der Quelle 4.99 3.94 3.18 4.13 4.26 2.95 Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 6. Textkompression 280 Allgemeines zur Textkompression LZB ist die beste LZ77-Variante, LZFG die beste LZ78-Variante. Die Werte wurden auf der Basis einer umfangreichen heterogenen Dokumentkollektion ermittelt (u.a. troff-Texte, ausführbare Programme, ASCII-Texte, Programmquellen, wissenschaftliche Daten, etc.). Textalgorithmen — FH Bonn-Rhein-Sieg, SS 02 281