Tokenizer
Warum dieses Tutorial ?
Bei meiner Suche im Netz habe ich sehr wenig Information über das Thema gefunden.
Das ist nicht gerechtfertigt, und daher möchte ich hier einen Einstieg bieten.
Wer sich das mal anschaut, wird sicher einige Ideen bekommen, was man damit noch so alles anstellen kann.
Mich hat das Thema inspiriert und deshalb möchte ich euch daran teilhaben lassen.
Was ist eigentlich ein Tokenizer ?
Der Begriff Token heisst wörtlich übersetzt Zeichen. Es ist sozusagen das kleinste Teilchen was wir kennen.
Ein Tokenizer macht also nichts anderes, als irgendeine Vorlage in seine einzelnen Zeichen zu zerlegen.
Dabei werden die einzelnen Zeichen auch noch untersucht und der Typ wird bestimmt.
Wenn wir also einen Tokenizer benötigen, dann geht es darum, einen Text in seine einzelnen "Zeichen" zu zerlegen, um mit diesen Zeichen irgendetwas anzufangen.
Wozu brauchen wir das ?
Nun, es gibt verschiedene Problemstellungen, die einen Tokenizer benötigen.
So wollen wir z.B. einen Text zerlegen und eine Wortliste erstellen. Dies machen alle Forenscripte, um die Suche zu beschleunigen.
Oder wir wollen irgendeinen Text parsen. Parsen heisst ja nichts anderes, als den Text zu übersetzen bzw. nach Schlüsselwörtern zu suchen und darauf zu reagieren.
Dabei ist es völlig unerheblich, ob es sich um PHP, SQL, HTML, CSS, CSV, XML etc. handelt. All diese Vorlagen basieren auf Texte, die Schlüsselwörter enthalten.
Man kann sich vorstellen, wenn man das alles händisch programmieren will, der Aufwand sehr gross ist.
Glücklicherweise hat PHP seit Version 4.3.3 den Tokenizer eingebaut, die wenigsten werden damit schonmal in Berührung gekommen sein.
Ich denke das wird sich nach diesem Tutorial ändern, denn der Tokenizer ist nicht nur genial, sondern auch genial einfach
Der PHP-Tokenizer
Der PHP-Tokenizer kennt gerade mal 2 Befehle:
token_get_all()
token_name()
Der erste Befehl zerlegt die gegebene Vorlage in ihre Tokens.
Der Rückgabewert ist ein Array.
Ist das entdeckte Token ein einzelnes Zeichen wie ",;:!?=" etc., so steht in dem Arrayindex nur dieses Zeichen.
Ist das Token ein Wort oder ein erkannter Teil, so steht in dem Arrayindex ein weiteres Array mit der id (was wurde erkannt) und dem Token.
Jetzt ein kleiner Trick, damit wir den Tokenizer auch dazu bekommen, alles zu parsen.
Der Tokenizer wurde dazu entwickelt, PHP-Text zu zerlegen. Nehmen wir einen normalen Text, so wird er vom Tokenizer als T_INLINE_HTML interpretiert und das entspricht einer Kommentarzeile.
Er würde also gar nicht den Text in die kleinstmöglichen Tokens zerlegen.
Also gaukeln wir dem Tokenizer einen Code vor.
Der Code beginnt mit <? und endet mit ?>. Jetzt funktioniert der Tokenizer mit jeder Vorlage !
Das einfachste ist, wir schauen uns ein kleines Beispiel an:
PHP-Code:
$arr=token_get_all("<?Hallo, schön hier !?>");
Wenn wir uns das Array $arr anschauen, sehen wir, wie der Tokenizer gearbeitet hat:
Code:
Array
(
[0] => Array
(
[0] => 353
[1] => Array
(
[0] => 304
[1] => Hallo
)
[2] => ,
[3] => Array
(
[0] => 356
[1] =>
)
[4] => Array
(
[0] => 304
[1] => schön
)
[5] => Array
(
[0] => 356
[1] =>
)
[6] => Array
(
[0] => 304
[1] => hier
)
[7] => Array
(
[0] => 356
[1] =>
)
[8] => !
[9] => Array
(
[0] => 355
[1] => ?>
)
)
Der Tokenizer hat die Vorlage in 10 Tokens aufgeteilt.
Sofort sieht man, das zweimal ein einzelnes Zeichen entdeckt wurde:
Element [2] und [8] - das sind die Zeichen , und !
Alle anderen Elemente sind Arrays. Wir sehen im Index [0] immer eine Zahl und im Index [1] das Token.
Was bedeuten denn die Zahlen ?
Das ist die ID des erkannten Tokens. Auffällig ist auch, das das Leerzeichen auch ein erkanntes Token darstellt.
Doch was für Tokens hat er denn jetzt erkannt ? Muss ich in einem schlauen Buch nach der Zahl suchen ?
Und hier kommt die 2. Tokenizer-Funktion zum Zuge :
token_name()
Sie gibt uns den (Konstanten-)Namen des Token zurück.
Wir basteln uns einen Mini-Parser und schauen uns das Ergebnis mal an:
PHP-Code:
<?php
$test="<?Hallo, schön hier !?>";
$arr=token_get_all($test);
foreach($arr as $token) {
if(!is_array($token)) { // kein Array, also ein einzelnes Zeichen
echo 'Zeichen gefunden :'.$token.'<br>';
} else {
//erkannte Tokens sind im zweidimensionalen Array
list($id,$tok)=$token;
echo 'Token gefunden '.$id.' ('.token_name($id).'): '.htmlentities($tok).'<br>';
}
}
?>
Die Ausgabe sieht so aus:
Code:
Token gefunden 353 (T_OPEN_TAG): <?
Token gefunden 304 (T_STRING): Hallo
Zeichen gefunden :,
Token gefunden 356 (T_WHITESPACE):
Token gefunden 304 (T_STRING): schön
Token gefunden 356 (T_WHITESPACE):
Token gefunden 304 (T_STRING): hier
Token gefunden 356 (T_WHITESPACE):
Zeichen gefunden :!
Token gefunden 355 (T_CLOSE_TAG): ?>
Wir sehen, das alle Worte als T_STRING erkannt werden.
Das Leerzeichen ist T_WHITESPACE.
Somit haben wir alle Grundlagen des Tokenizers kennengelernt und machen uns an zwei Praxisbeispiele.
1. Ein Wortzähler
2. Ein Parser mit Highlight-Funktion
Die Umsetzung zeigt nur den Ansatz, das kann man beliebig erweitern.
Der Wortzähler
In diesem Beispiel geht es darum, die Worte zu zählen und somit ein Dictonary zu erstellen.
Als Vorlage nehmen wir uns einen beliebigen Text.
Der Quelltext könnte jetzt so aussehen:
PHP-Code:
<?php
$test='<?
Nach dem Rückzug von Heide Simonis deutet in Schleswig-Holstein vieles auf eine Große Koalition unter CDU-Mann Carstensen hin. Denn die FDP hat kein Interesse an einem Ampelbündnis, und sowohl Südschleswigscher Wählerbund als auch SPD erachten das Modell einer tolerierten Minderheitsregierung für gescheitert.
Simonis: Abschied nach zwölf Jahren
Kiel - "Dieses gemeinsame Projekt ist jetzt durch einen feigen Heckenschützen zu Fall gebracht worden", hieß es heute in einer Erklärung der SSW-Landtagsabgeordneten Anke Spoorendonk und Lars Harms sowie der Landesvorsitzenden Gerda Eichhorn. Simonis sei eine verlässliche Partnerin gewesen, die einer Minderheitsregierung Tatkraft und Stabilität verliehen hätte.
Auch die SPD will vorerst keinen erneuten Versuch für ein Dreier-Bündnis mit dem SSW unternehmen. Solange der Abweichler nicht bekannt sei, könne er keinen Kandidaten bei nur einer Stimme Mehrheit ins Rennen um die Wahl eines neuen Ministerpräsidenten schicken, sagte SPD-Fraktionschef Lothar Hay am Rande einer Parteiratssitzung in Kiel. "Das wäre politischer Kamikaze-Flug." Hay bekräftigte, dass mit allen Parteien Sondierungsgespräche über die Bildung einer neuen Landesregierung geführt werden sollten. Bereits nächste Woche wolle sich die SPD mit der CDU darüber unterhalten. Die größte Differenz zur CDU liege in der Bildungspolitik.
?>';
$woerter=Array();
$arr=token_get_all($test);
foreach($arr as $token) {
if(!is_array($token)) { // kein Array, also ein einzelnes Zeichen
//ignorieren
} else {
//erkannte Tokens sind im zweidimensionalen Array
list($id,$tok)=$token;
if(token_name($id)=="T_STRING") // uns interessieren nur die Wörter
$woerter[$tok]++;
}
}
//Wortliste sortieren
ksort($woerter);
//Wortliste anzeigen
foreach($woerter as $var => $val) {
echo "Wort '$var' kommt $val mal vor<br>";
}
?>
Ein Parser mit Highlight-Funktion
Vorlage ist ein kleines Perlscript.
Werden Schlüsselwörter gefunden, färben wir
blau ein.
Wird ein Kommentar gefunden, färben wir
grün ein.
Wird ein durch Anführungszeichen eingeschlossenen String gefunden, färben wir
rot ein.
die Vorlage:
Code:
#!usr/bin/perl
print "Content-type: text/html\n\n";
print "Hallo,\n";
print "Ich war ein Perl-Script!";
Und hier der Parser
PHP-Code:
<?php
//Perlskript laden
$script="<? ".file_get_contents("test.pl")."?>";
$arr=token_get_all($script);
foreach($arr as $token) {
if(!is_array($token)) { // kein Array, also ein einzelnes Zeichen
echo $token;
if($token==";") echo "<br>";
} else {
//erkannte Tokens sind im zweidimensionalen Array
list($id,$tok)=$token;
switch(token_name($id)) {
case "T_OPEN_TAG":
case "T_CLOSE_TAG":
//ignorieren
break;
case "T_COMMENT": // Kommentar
echo '<span style="color:green;">'.$tok.'</span><br>';
break;
case "T_CONSTANT_ENCAPSED_STRING": //Ein String in Anführungszeichen
echo '<span style="color:red;">'.$tok.'</span>';
break;
case "T_PRINT": // der Befehl print
echo '<span style="color:blue;">'.$tok.'</span>';
break;
default: // alles andere
echo $tok;
}
}
}
?>
Ich hoffe durch dieses Tutorial einige Anregungen gegeben zu haben.
Eine vollständige Liste der eingebauten Tokens gibt es
hier
Viel Spass beim experimentieren !