2.3Zerlegen von Zeichenketten
Die Java-Bibliothek bietet eine Reihe von Möglichkeiten zum Zerlegen von Zeichenfolgen, wobei split(…) von String und Grundzüge vom Scanner schon im ersten Band in Kapitel 4, »Datenstrukturen und Algorithmen«, vorgestellt wurden. Fassen wir zusammen:
Pattern: Zerlegt Zeichenketten zum Beispiel mit split(CharSequence input) und liefert ein Feld von Strings zurück. Reguläre Ausdrücke, die das Pattern-Objekt repräsentieren, bestimmen die Trennpunkte.
String: eine einfache Fassade für die Pattern-Klasse und deren split(…)-Methode
Scanner: komfortable Klasse zum Ablaufen von Eingaben aus beliebigen Quellen, etwas aus Dateien
StringTokenizer: Der Klassiker aus Java 1.0. Delimiter sind nur einzelne Zeichen.
BreakIterator: Findet Zeichen-, Wort-, Zeilen- oder Satzgrenzen.
2.3.1Zerlegen von Zeichensequenzen über String oder Pattern
Die Klassen String und Pattern bieten beide eine split(…)-Methode, doch die eigentliche Arbeit macht Pattern, denn String delegiert nur – wie auch bei matches(…) – an das Pattern-Objekt:
Listing 2.9java/lang/String.java, Ausschnitt
return Pattern.compile( regex ).split( this, limit );
}
public String[] split( String regex ) {
return split( regex, 0 );
}
Am Quellcode ist zu erkennen, dass jeder Methodenaufruf von split(…) auf dem String-Objekt zu einem neuen Pattern-Objekt führt. Das ist nicht ganz billig.
Eine neu in Java 8 eingeführte Methode splitAsStream(CharSequence) liefert kein Feld als Rückgabe, sondern einen Stream von Strings.
[+]Tipp
Beim mehrmaligen Zerlegen (von unterschiedlichen Eingaben) mit dem gleichen Zerlege-Muster sollte direkt ein Pattern-Objekt mit der dort angesiedelten split(…)-Methode verwendet werden.
implements Serializable
String[] split(CharSequence input)
Zerlegt die Zeichenfolge input in Teilzeichenketten, wie es das aktuelle Pattern-Objekt befiehlt.String[] split(CharSequence input, int limit)
Wie split(CharSequence), doch nur höchstens limit viele Teilzeichenketten.Stream<String> splitAsStream(CharSequence input)
Liefert die Tokens nicht als komplettes Array zurück, sondern als Stream von Strings. Die Methode ist nützlich in Kombination mit Lambda-Ausdrücken, da Stream-Objekte eine ganze Reihe von interessanten Methoden bieten, etwa zum Filtern von Elementen.
[zB]Beispiel
Gib alle Strings aus, die länger als ein Zeichen sind:
.splitAsStream( "99 12 c" )
.filter( s -> s.length() > 1 )
.forEach( s -> System.out.println( s ) );
2.3.2Mehr vom Scanner
Der Konstruktor vom Scanner nimmt unterschiedliche Textquellen an, etwa Strings, Path oder Datenströme, und ermöglicht dann das Durchlaufen mittels hasNextXXX()/nextXXX()-Methoden. Soweit die Grundlagen.
Trennzeichen definieren
useDelimiter(…) setzt für die folgenden Filter-Vorgänge den Delimiter.
[zB]Beispiel
Der String s enthält eine Zeile wie a := b. Uns interessieren der linke und der rechte Teil:
Scanner scanner = new Scanner( s ).useDelimiter( "\\s*:=\\s*" );
System.out.printf( "%s = %s", scanner.next(), scanner.next() );
// Url = http://www.tutego.com
Um nur lokal für das nächste Zerlegen einen Trenner zu setzen, lässt sich next() mit einem Argument ausstatten, also mit next(String) oder next(Pattern) ein Trennmuster angeben. hasNext(String) bzw. hasNext(Pattern) liefern true, wenn das nächste Token dem Muster entspricht.
In einer Zeile und im ganzen Text suchen
Mit findInLine(String) bzw. findInLine(Pattern) wird der Scanner angewiesen, nach einem Muster nur bis zum nächsten Zeilenendezeichen zu suchen; Delimiter ignoriert er.
[zB]Beispiel
Suche mit findInLine(…) nach einem Muster:
Scanner scanner = new Scanner( text ).useDelimiter( "-" );
System.out.println( scanner.findInLine( "Wald" ) ); // null
System.out.println( scanner.findInLine( "ete" ) ); // "ete"
System.out.println( scanner.next() ); // "l" "gingen"
System.out.println( scanner.next() ); // "durch"
findInLine(…) ist eine Methode, die der Methode indexOf(…) von String ähnelt, nur kann hier nach einem regulären Ausdruck gesucht werden. Und statt des Index gibt die Methode den gematchten String zurück oder null, falls es keinen Match gab, und der Scanner positioniert nach dieser Fundstelle. Mit regulären Ausdrücken wird das Ganze leistungsfähig.
[zB]Beispiel
Finde alles, was in HTML zwischen <b></b> steht:
Scanner scanner = new Scanner( s );
for ( String match = null; (match = scanner.findInLine( "<b>(.+?)</b>" )) != null; )
System.out.println( match );
Die Ausgabe ist: »<b>essen</b>« und dann »<b>trinken</b>«. Interessieren wir uns nur für das »Innere«, muss auf die Match-Gruppe zugegriffen werden. Kurz skizziert:
System.out.println( scanner.match().group( 1 ) );
Die Ausgabe ist dann »essen« und »trinken«.
Mit findWithinHorizon(Pattern, int) bzw. findWithinHorizon(String, int) lässt sich eine Obergrenze von Code-Points (vereinfacht ausgedrückt von Zeichen) angeben, die maximal berücksichtigt werden sollen, und zudem sucht die Methode im ganzen String und ignoriert Zeilenendezeichen. Hätten wir etwa ein Zeilenumbruchzeichen im String wie folgt, hätten wir nur »essen« gefunden:
Anders ist da findWithinHorizon(…); diese Methode ignoriert Zeilenendezeichen.
[zB]Beispiel
Finde alles Wichtige, Teil 2:
Scanner sc = new Scanner( s );
for ( String match = null; (match = sc.findWithinHorizon( "<b>(.+?)</b>", 0 ))
!= null; ) {
System.out.println( match );
}
Liefert die Methode in dieser Grenze kein Token, liefert sie null und setzt auch den Positionszeiger nicht weiter. Ist die Grenze 0, gibt es keinen Suchhorizont, und er ist unendlich.
[zB]Beispiel
System.out.println( sc.findWithinHorizon( "\\d+", 2 ) ); // null
System.out.println( sc.findWithinHorizon( "\\d+", 20 ) ); // 12
Scanner und Gruppen der regulären Ausdrücke
Reguläre Ausdrücke machen die Klasse Scanner leistungsfähiger, und erst wenn Entwickler vollständig die Verzahlung verinnerlicht haben, kann die Klasse ihr volles Potenzial entfalten. An einzelnen Methoden wie findWithinHorizon(…) haben wir Beispiele für reguläre Ausdrücke bei Suchen gesehen, aber die Anzahl der Methoden, die mit regulären Ausdrücken zu tun haben, ist doch überraschend groß. Zusammenfasst:
String findInLine(Pattern pattern)
String findInLine(String pattern)
String findWithinHorizon(Pattern pattern, int horizon)
String findWithinHorizon(String pattern, int horizon)
boolean hasNext(Pattern pattern)
boolean hasNext(String pattern)
String next(Pattern pattern)
String next(String pattern)
Scanner skip(Pattern pattern)
Scanner skip(String pattern)
Scanner useDelimiter(Pattern pattern)
Scanner useDelimiter(String pattern)
Pattern delimiter()
Immer dann, wenn ein Scanner mit einem regulären Ausdruck konfiguriert wurde, wird intern der Zustand vom dafür zugewiesenen Matcher aktualisiert. Die Scanner-Methode match() liefert einen MatchResult der letzten Operation, allerdings folgt eine IllegalStateException, wenn es keinen Match gab oder der letzte Match nicht erfolgreich war.
In Kombination mit findWithinHorizon(…) ist ein MatchResult sehr nützlich, denn er erlaubt den Zugriff auf die Gruppen, die in regulären Ausdrücken eingesetzt werden können, sodass es möglich ist, ganz gezielt auf Teil-Strings zuzugreifen. Wir wollen das an einem Beispiel nutzen, in dem wir das Windows-Programm netstat aufrufen und die Ausgabe zerlegen. Normalerweise sieht die Ausgabe so aus:
Aktive Verbindungen
Proto Lokale Adresse Remoteadresse Status
TCP 127.0.0.1:16709 127.0.0.1:49159 HERGESTELLT
TCP 127.0.0.1:19872 127.0.0.1:49176 HERGESTELLT
TCP 127.0.0.1:49159 127.0.0.1:16709 HERGESTELLT
…
Es ist eine tabellenförmige Auflistung von Verbindungen von dem eigenen Rechner zu anderen Rechnern.
Ein eigenes Java-Programm soll netstat aufrufen, die Spalten auslesen und in einem eigenen Format ausgeben. Das Erkennen von Spalten übernehmen Gruppen in dem regulären Ausdruck "(TCP|UDP)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)". Die erste Gruppe fängt TCP oder UDP, dann folgt nach einem beliebigen Weißraum in der zweiten Gruppe die eigene IP-Adresse mit Port usw.
Listing 2.10Netstat.java, main()
Process p = builder.start();
try ( Scanner scanner = new Scanner( p.getInputStream() ) ) {
Pattern pattern = Pattern.compile( "(TCP|UDP)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)" );
while ( scanner.findWithinHorizon( pattern, 0 ) != null )
System.out.printf( "%-6s %-22s %-22s %s%n", scanner.match().group( 1 ), scanner.match().group( 2 ),
scanner.match().group( 3 ), scanner.match().group( 4 ) );
}
Die eigene Ausgabe beginnt damit:
TCP 127.0.0.1:19872 127.0.0.1:49176 HERGESTELLT
TCP 127.0.0.1:49159 127.0.0.1:16709 HERGESTELLT
…
Die Designer der Java-Bibliotheken hätten findInLine(…)/findWithinHorizon(…) natürlich auch einen MatchResult zurückgeben lassen können, doch empfanden sie die Rückgabe eines Strings wohl als nützlicher.
Landessprachen
Auch ist die Scanner-Klasse dazu in der Lage, die Dezimalzahlen unterschiedlicher Sprachen zu erkennen.
[zB]Beispiel
Mit dem passenden Locale-Objekt erkennt der Scanner bei nextDouble() auch Fließkommazahlen mit Komma, etwa "12,34":
System.out.println( scanner.nextDouble() ); // 12.34
Das klingt logisch, funktioniert aber bei einem deutschsprachigen Betriebssystem in der Regel auch ohne useLocale(Locale.GERMAN). Der Grund ist einfach: Der Scanner setzt das Locale vorher standardmäßig auf Locale.getDefault(), und bei auf Deutsch eingestellten Betriebssystemen ist das eben Locale.GERMAN. Andersherum bedeutet das, dass eine in englischer Schreibweise angegebene Zahl wie 12.34 nicht erkannt wird und der Scanner eine java.util.InputMismatchException meldet.
implements Iterator<String>, Closeable
Scanner useLocale(Locale locale)
Setzt die Sprache zum Erkennen der lokalisierten Zahlen, insbesondere der Fließkommazahlen.Locale locale()
Liefert die eingestellte Sprache.
IO-Fehler während des Parsens
Bezieht der Scanner die Daten von einem Readable, kann es Ein-/Ausgabefehler in Form von IOExceptions geben. Methoden wie next() geben diese Fehler nicht weiter, sondern fangen sie ab und speichern sie intern. Die Methode ioException() liefert dann das letzte IOException-Objekt oder null, falls es keinen Fehler gab.
2.3.3Die Klasse StringTokenizer *
Die Klasse StringTokenizer zerlegt ebenfalls eine Zeichenkette in Tokens. Der StringTokenizer ist jedoch auf einzelne Zeichen als Trennsymbole beschränkt, während die Methode split(…) aus der Klasse Pattern bzw. String einen regulären Ausdruck zur Beschreibung der Trennsymbole erlaubt. Es sind keine Zeichenfolgen wie »:=« denkbar.
[zB]Beispiel
Um einen String mithilfe eines StringTokenizer-Objekts zu zerlegen, wird dem Konstruktor der Klasse der zu unterteilende Text als Argument übergeben:
StringTokenizer tokenizer = new StringTokenizer( s );
while ( tokenizer.hasMoreTokens() )
System.out.println( tokenizer.nextToken() );
Der Text ist ausschließlich ein Objekt vom Typ String.
Um den Text abzulaufen, gibt es die Methoden nextToken() und hasMoreTokens().[ 24 ](Die Methode hasMoreElements() ruft direkt hasMoreTokens() auf und wurde nur implementiert, da ein StringTokenizer die Schnittstelle Enumeration implementiert.) Die Methode nextToken() liefert das nächste Token im String. Ist kein Token mehr vorhanden, wird eine NoSuchElementException ausgelöst. Damit wir frei von diesen Überraschungen sind, können wir mit der Methode hasMoreTokens() nachfragen, ob noch ein weiteres Token vorliegt.
In der Voreinstellung sind Tabulator, Leerzeichen und Zeilentrenner die Delimiter. Sollen andere Zeichen als die voreingestellten Trenner den Satz zerlegen, kann dem Konstruktor als zweiter String eine Liste von Trennern übergeben werden. Jedes Zeichen, das in diesem String vorkommt, fungiert als einzelnes Trennzeichen:
Neben den beiden Konstruktoren existiert noch ein dritter, der auch die Trennzeichen als eigenständige Bestandteile bei nextToken() übermittelt.
implements Enumeration<Object>
StringTokenizer(String str, String delim, boolean returnDelims)
Ein String-Tokenizer für str, wobei jedes Zeichen in delim als Trennzeichen gilt. Ist returnDelims gleich true, so sind auch die Trennzeichen Tokens der Aufzählung.StringTokenizer(String str, String delim)
Ein String-Tokenizer für str, wobei alle Zeichen in delim als Trennzeichen gelten. Entspricht dem Aufruf von this(str, delim, false).StringTokenizer(String str)
Ein String-Tokenizer für str. Entspricht dem Aufruf von this(str, " \t\n\r\f", false). Die Trennzeichen sind Leerzeichen, Tabulator, Zeilenende und Seitenvorschub.boolean hasMoreTokens()
boolean hasMoreElements()
Testet, ob ein weiteres Token verfügbar ist. hasMoreElements() implementiert die Methode der Schnittstelle Enumeration, aber beide Methoden sind identisch.String nextToken()
Object nextElement()
Liefert das nächste Token vom String-Tokenizer. nextElement() existiert nur, damit der Tokenizer als Enumeration benutzt werden kann. Der weniger spezifische Ergebnistyp Object macht eine Typumwandlung erforderlich.String nextToken(String delim)
Setzt die Delimiter-Zeichen neu und liefert anschließend das nächste Token.int countTokens()
Zählt die Anzahl der noch möglichen nextToken()-Methodenaufrufe, ohne die aktuelle Position zu berühren. Der Aufruf der Methode ist nicht billig.
2.3.4BreakIterator als Zeichen-, Wort-, Zeilen- und Satztrenner *
Benutzer laufen Zeichenketten aus ganz unterschiedlichen Gründen ab. Ein Anwendungsszenario ist das Ablaufen eines Strings Zeichen für Zeichen. In anderen Fällen sind nur einzelne Wörter interessant, die durch Wort- oder Satztrenner separiert sind. In wieder einem anderen Fall ist eine Textausgabe auf eine bestimmte Zeilenlänge gewünscht.
Zum Zerlegen von Zeichenfolgen sieht die Standardbibliothek im Java-Paket java.text die Klasse BreakIterator vor. Einen konkreten Iterator erzeugen diverse statische Methoden, die optional auch nach speziellen Kriterien einer Sprache trennen. Wenn keine Sprache übergeben wird, wird automatisch die Standardsprache verwendet.
implements Cloneable
static BreakIterator getCharacterInstance()
static BreakIterator getCharacterInstance(Locale where)
Trennt nach Zeichen. Vergleichbar mit einer Iteration über charAt().static BreakIterator getSentenceInstance()
static BreakIterator getSentenceInstance(Locale where)
Trennt nach Sätzen. Delimiter sind übliche Satztrenner wie ».«, »!«, »?«.static BreakIterator getWordInstance()
static BreakIterator getWordInstance(Locale where)
Trennt nach Wörtern. Trenner wie Leerzeichen und Satzzeichen gelten ebenfalls als Wörter.static BreakIterator getLineInstance()
static BreakIterator getLineInstance(Locale where)
Trennt nicht nach Zeilen, wie der Name vermuten lässt, sondern ebenfalls nach Wörtern. Nur werden Satzzeichen, die am Wort »hängen«, zum Wort hinzugezählt. Praktisch ist dies für Algorithmen, die Textblöcke in eine bestimmte Breite bringen wollen. Ein Beispiel für die drei Typen zeigt das gleich folgende Programm.
[»]Hinweis
Auf den ersten Blick ergibt ein BreakIterator von getCharacterInstance(…) keinen großen Sinn, denn für das Ablaufen einer Zeichenkette ließe sich viel einfacher eine Schleife nehmen und mit charAt(…) arbeiten. Der BreakIterator kann jedoch korrekt mit Unicode 4 umgehen, wo zwei char ein Unicode 4-Zeichen bilden. Zum zeichenweisen Iterieren über Strings ist auch CharacterIterator eine gute Lösung.
Beispiel für die drei BreakIterator-Typen
Das nächste Beispiel zeigt, wie ohne großen Aufwand durch Zeichenketten gewandert werden kann. Die Verwendung eines String-Tokenizers ist nicht nötig. Unsere statische Hilfsmethode out(…) gibt die Abschnitte der Zeichenkette bezüglich eines BreakIterator aus:
Abbildung 2.2Klassendiagramm vom BreakIterator
Listing 2.11BreakIteratorDemo.java, out()
iter.setText( s );
for ( int last = iter.first(),next = iter.next();
next != BreakIterator.DONE;
last = next, next = iter.next() ) {
CharSequence part = s.subSequence( last, next );
if ( Character.isLetterOrDigit( part.charAt( 0 ) ) )
System.out.println( part );
}
}
Einmal sollen die Wörter und einmal die Sätze ausgegeben werden:
Listing 2.12BreakIteratorDemo.java, main()
String helmutKohl1 = "Ich weiß, dass ich 1945 fünfzehn war und 1953 achtzehn.",
helmutKohl2 = "Das ist eine klassische journalistische Behauptung. " +
"Sie ist zwar richtig, aber sie ist nicht die Wahrheit.";
BreakIterator sentenceIter = BreakIterator.getSentenceInstance();
BreakIterator wordIter = BreakIterator.getWordInstance();
BreakIterator lineIter = BreakIterator.getLineInstance();
out( helmutKohl1, sentenceIter );
out( helmutKohl2, sentenceIter );
System.out.println( "-----------------" );
out( helmutKohl1, wordIter );
out( helmutKohl2, wordIter );
System.out.println( "-----------------" );
out( helmutKohl1, lineIter );
out( helmutKohl2, lineIter );
}
Die Ausgabe enthält (skizziert):
Das ist eine klassische journalistische Behauptung.
Sie ist zwar richtig, aber sie ist nicht die Wahrheit.
-----------------
Ich
weiß
…
die
Wahrheit
-----------------
Ich
weiß,
…
die
Wahrheit.
Im letzten Beispiel ist sehr gut zu sehen, dass die Wörter am Ende ihre Leer- und Satzzeichen behalten.
2.3.5StreamTokenizer *
Die Klasse StreamTokenizer aus dem java.io-Paket arbeitet noch spezialisierter als die StringTokenizer-Klasse aus dem util-Paket, und die Klasse Scanner kommt der Klasse StreamTokenizer schon sehr nahe. Im Gegensatz zum Scanner beachtet ein StreamTokenizer keine Unicode-Eingabe, sondern nur Zeichen aus dem Bereich von \u0000 bis \u00FF, kann aber mit Kommentaren umgehen.
Während des Parsens erkennt der Tokenizer bestimmte Merkmale, so unter anderem Bezeichner (etwa Schlüsselwörter), Zahlen, Strings in Anführungszeichen und verschiedene Kommentararten (C-Stil oder C++-Stil). Verschiedene Java-Tools von Oracle verwenden intern einen StreamTokenizer, um ihre Eingabedateien zu verarbeiten, etwa das Policy-Tool für die Rechteverwaltung. Der Erkennungsvorgang wird anhand einer Syntaxtabelle überprüft. Diese Tabelle enthält zum Beispiel die Zeichen, die ein Schlüsselwort identifizieren, oder die Zeichen, die Trennzeichen sind. Jedes gelesene Zeichen wird dann keinem, einem oder mehreren Attributen zugeordnet. Diese Attribute fallen in die Kategorie Trennzeichen, alphanumerische Zeichen, Zahlen, Hochkommata bzw. Anführungszeichen oder Kommentarzeichen.
Zur Benutzung der Klasse wird zunächst ein StreamTokenizer-Objekt erzeugt, und dann werden die Syntaxtabellen initialisiert. Ob Kommentarzeilen überlesen werden sollen, wird durch
st.slashStarComments( true ); /* Kommentar */
gesteuert. Die erste Methode überliest im Eingabestrom alle Zeichen bis zum Return. Die zweite Methode überliest nur alles bis zum Stern/Slash. Geschachtelte Kommentare sind hier nicht möglich.
Beim Lesen des Datenstroms mit nextToken() kann über bestimmte Flags erfragt werden, ob im Stream ein Wort bzw. Bezeichner (TT_WORD), eine Zahl (TT_NUMBER), das Ende der Datei (TT_EOF) oder das Ende der Zeile (TT_EOL) vorliegt. Wichtig ist, eolIsSignificant(true) zu setzen, da andernfalls der StreamTokenizer nie ein TT_EOL findet. Wurde ein Wort erkannt, dann werden alle Zeichen in Kleinbuchstaben konvertiert. Dies lässt sich über die Methode lowerCaseMode(boolean) einstellen. Nach der Initialisierung eines StreamTokenizer-Objekts wird normalerweise so lange nextToken() aufgerufen, bis die Eingabe keine neuen Zeichen mehr hergibt, also ein TT_EOF-Token erkannt wurde.
Ein Beispiel: Die folgende Klasse liest die Eingabe vom Netzwerk und gibt die erkannten Textteile aus:
Listing 2.13com/tutego/insel/io/stream/StreamTokenizerDemo.java, main()
Reader reader = new InputStreamReader( url.openStream() );
StreamTokenizer st = new StreamTokenizer( reader );
// st.slashSlashComments( true ); */
st.slashStarComments( true );
st.ordinaryChar( '/' );
st.parseNumbers();
st.eolIsSignificant( true );
for ( int tval; (tval = st.nextToken()) != StreamTokenizer.TT_EOF; ) {
if ( tval == StreamTokenizer.TT_NUMBER )
System.out.println( "Nummer: " + st.nval );
else if ( tval == StreamTokenizer.TT_WORD )
System.out.println( "Wort: " + st.sval );
else if ( tval == StreamTokenizer.TT_EOL )
System.out.println( "Ende der Zeile" );
else
System.out.println( "Zeichen: " + (char) st.ttype );
}
StreamTokenizer(Reader r)
Erzeugt einen Tokenizer, der den Datenstrom zerlegt. Der Konstruktor, der das Ganze auch mit einem InputStream macht, ist veraltet.void resetSyntax()
Reinitialisiert die Syntaxtabelle des Tokenizers, sodass kein Zeichen eine Sonderbehandlung genießt. Mit ordinaryChar() lässt sich das Verhalten eines Zeichens bestimmen.void wordChars(int low, int hi)
Zeichen im Bereich von low <= c <= hi werden als Bestandteile von Wörtern erkannt, dementsprechend zusammengefasst und als Word-Token übergeben.[ 25 ](Dass der Endwert – wie sonst bei den Intervallangaben üblich – inklusive und nicht exklusive ist, stellt einen Stilbruch dar.)void whitespaceChars(int low, int hi)
Zeichen im Bereich von low <= c <= hi werden als Trennzeichen erkannt.void ordinaryChars(int low, int hi)
Zeichen im Bereich von low <= c <= hi genießen keine Sonderbehandlung und werden als normale Zeichen einzeln behandelt.void ordinaryChar(int ch)
Das Zeichen besitzt keine zusätzliche Funktion, ist zum Beispiel kein Kommentarzeichen, Trennsymbol oder Nummernzeichen. Spezialform für ordinaryChars(ch, ch).void parseNumbers()
Zahlen (Zahl-Literale) sollen vom Tokenizer erkannt werden. In der Syntaxtabelle gelten die zwölf Zeichen 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, . und – als numerisch. Liegt eine Ganz- oder Fließkommazahl vor, wird der Zahlenwert in nval abgelegt, und das Token ergibt im Attribut ttype den Wert TT_NUMBER.void commentChar(int ch)
Gibt das Zeichen an, das einen einzeiligen Kommentar einleitet. Alle nachfolgenden Zeichen werden dann bis zum Zeilenende ignoriert. So ließen sich beispielsweise FORTRAN-Kommentare nach commentChar('C') überlesen.void slashStarComments(boolean flag)
Der Tokenizer soll Kommentare im C-Stil (/* Müll */) erkennen oder nicht.void slashSlashComments(boolean flag)
Der Tokenizer soll Kommentare im C++-Stil (// Zeile ) erkennen oder nicht.void lowerCaseMode(boolean fl)
Liegt in ttype ein Token vom Typ TT_WORD vor, wird es automatisch in Kleinschreibweise konvertiert, falls fl gleich true ist.int nextToken() throws IOException
Liefert das nächste Token im Datenstrom. Der Typ des Tokens wird im Attribut ttype hinterlegt. Zusätzliche Informationen befinden sich im Attribut nval (Nummer) oder sval (Zeichenkette). In der Regel wird so lange geparst, bis das Token TT_EOF zurückgegeben wird.void pushBack()
Legt das aktuelle Token in den Eingabestrom zurück. Ein Aufruf von nextToken() liefert erneut den aktuellen Wert im Attribut ttype und ändert nval oder sval nicht.int lineno()
Liefert die aktuelle Zeilennummer in der Eingabedatei.
Erweiterungen und Schwächen
Obwohl die nextToken()-Methode eine ganze Reihe von Konvertierungen durchführt, erkennt sie keine in der Exponentialdarstellung geschriebenen Zahlen. Bei einer Gleitkommazahl wie –31.415E-1 versagt die Konvertierung und liefert nur –31.415 als Token vom Typ TT_NUMBER. Da StreamTokenizer nicht final ist, kann jedoch jeder die Klasse so erweitern, dass sie zum Beispiel TT_FLOAT_NUMBER bei einer Gleitkommazahl liefert. Dazu ist die öffentliche Methode nextToken() zu überschreiben und vielleicht auch noch toString(). Die Erweiterung von nextToken() erfordert jedoch etwas Arbeit, da das Original ein wenig unübersichtlich ist.