Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.
 
Inhaltsverzeichnis
Vorwort
1 Neues in Java 8 und Java 7
2 Fortgeschrittene String-Verarbeitung
3 Threads und nebenläufige Programmierung
4 Datenstrukturen und Algorithmen
5 Raum und Zeit
6 Dateien, Verzeichnisse und Dateizugriffe
7 Datenströme
8 Die eXtensible Markup Language (XML)
9 Dateiformate
10 Grafische Oberflächen mit Swing
11 Grafikprogrammierung
12 JavaFX
13 Netzwerkprogrammierung
14 Verteilte Programmierung mit RMI
15 RESTful und SOAP-Web-Services
16 Technologien für die Infrastruktur
17 Typen, Reflection und Annotationen
18 Dynamische Übersetzung und Skriptsprachen
19 Logging und Monitoring
20 Sicherheitskonzepte
21 Datenbankmanagement mit JDBC
22 Java Native Interface (JNI)
23 Dienstprogramme für die Java-Umgebung
Stichwortverzeichnis

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java SE 8 Standard-Bibliothek von Christian Ullenboom
Das Handbuch für Java-Entwickler
Buch: Java SE 8 Standard-Bibliothek

Java SE 8 Standard-Bibliothek
Pfeil 2 Fortgeschrittene String-Verarbeitung
Pfeil 2.1 Erweitere Zeicheneigenschaften
Pfeil 2.1.1 isXXX(….)-Methoden
Pfeil 2.1.2 Unicode-Blöcke
Pfeil 2.1.3 Unicode-Skripte
Pfeil 2.2 Reguläre Ausdrücke
Pfeil 2.2.1 Pattern.matches(…) bzw. String#matches(…)
Pfeil 2.2.2 Die Klassen Pattern und Matcher
Pfeil 2.2.3 Finden und nicht matchen
Pfeil 2.2.4 Gruppen
Pfeil 2.2.5 Gierige und nicht gierige Operatoren *
Pfeil 2.2.6 Mit MatchResult alle Ergebnisse einsammeln *
Pfeil 2.2.7 Suchen und Ersetzen mit Mustern
Pfeil 2.2.8 Hangman Version 2
Pfeil 2.3 Zerlegen von Zeichenketten
Pfeil 2.3.1 Zerlegen von Zeichensequenzen über String oder Pattern
Pfeil 2.3.2 Mehr vom Scanner
Pfeil 2.3.3 Die Klasse StringTokenizer *
Pfeil 2.3.4 BreakIterator als Zeichen-, Wort-, Zeilen- und Satztrenner *
Pfeil 2.3.5 StreamTokenizer *
Pfeil 2.4 Zeichenkodierungen, XML/HTML-Entities, Base64 *
Pfeil 2.4.1 Unicode und 8-Bit-Abbildungen
Pfeil 2.4.2 Kodierungen über die Klasse String vornehmen
Pfeil 2.4.3 Das Paket java.nio.charset und der Typ Charset
Pfeil 2.4.4 Konvertieren mit OutputStreamWriter-/InputStreamReader-Klassen
Pfeil 2.4.5 XML/HTML-Entities ausmaskieren
Pfeil 2.4.6 Base64-Kodierung
Pfeil 2.5 Ausgaben formatieren
Pfeil 2.5.1 Die Formatter-Klasse *
Pfeil 2.5.2 Formatieren mit Masken *
Pfeil 2.5.3 Format-Klassen
Pfeil 2.5.4 Zahlen, Prozente und Währungen mit NumberFormat und DecimalFormat formatieren *
Pfeil 2.5.5 MessageFormat und Pluralbildung mit ChoiceFormat
Pfeil 2.6 Sprachabhängiges Vergleichen und Normalisierung *
Pfeil 2.6.1 Die Klasse Collator
Pfeil 2.6.2 Effiziente interne Speicherung für die Sortierung
Pfeil 2.6.3 Normalisierung
Pfeil 2.7 Phonetische Vergleiche *
Pfeil 2.8 Zum Weiterlesen
 
Zum Seitenanfang

2.3Zerlegen von Zeichenketten Zur vorigen ÜberschriftZur nächsten Überschrift

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.

 
Zum Seitenanfang

2.3.1Zerlegen von Zeichensequenzen über String oder Pattern Zur vorigen ÜberschriftZur nächsten Überschrift

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

public String[] split( String regex, int limit ) {
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.

final class java.util.regex.Pattern
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:

Pattern.compile( " " )
.splitAsStream( "99 12 c" )
.filter( s -> s.length() > 1 )
.forEach( s -> System.out.println( s ) );
 
Zum Seitenanfang

2.3.2Mehr vom Scanner Zur vorigen ÜberschriftZur nächsten Überschrift

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:

String s = "Url := http://www.tutego.com";
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:

String text = "Hänsel-und-Gretel\ngingen-durch-den-Wald";
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:

String s = "Wichtig: <b>essen</b> und <b>trinken</b>!";
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:

while ( sc.findInLine( "<b>(.+?)</b>" ) != null )
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:

String s = "Wichtig: <b>essen</b>\nund <b>trinken</b>!";

Anders ist da findWithinHorizon(…); diese Methode ignoriert Zeilenendezeichen.

[zB]Beispiel

Finde alles Wichtige, Teil 2:

String s = "Wichtig: <b>essen</b>\n<b>trinken</b>!";
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

Scanner sc = new Scanner( "Laber Rhabarber: 12" );
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:

$ netstat -n

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()

ProcessBuilder builder = new ProcessBuilder( "netstat", "-n" );
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: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

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":

Scanner scanner = new Scanner( "12,34" ).useLocale( Locale.GERMAN );
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.

final class java.util.Scanner
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.

 
Zum Seitenanfang

2.3.3Die Klasse StringTokenizer * Zur vorigen ÜberschriftZur nächsten Überschrift

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:

String s = "Faulheit ist der Hang zur Ruhe ohne vorhergehende Arbeit";
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:

StringTokenizer st = new StringTokenizer( "Blue=0000ff\nRed:ff0000\n", "=:\n" );

Neben den beiden Konstruktoren existiert noch ein dritter, der auch die Trennzeichen als eigenständige Bestandteile bei nextToken() übermittelt.

class java.util.StringTokenizer
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.

 
Zum Seitenanfang

2.3.4BreakIterator als Zeichen-, Wort-, Zeilen- und Satztrenner * Zur vorigen ÜberschriftZur nächsten Überschrift

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.

abstract class java.text.BreakIterator
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:

Klassendiagramm vom BreakIterator

Abbildung 2.2Klassendiagramm vom BreakIterator

Listing 2.11BreakIteratorDemo.java, out()

static void out( String s, BreakIterator iter ) {
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()

public static void main( String[] args ) {
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):

Ich weiß, dass ich 1945 fünfzehn war und 1953 achtzehn.
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.

 
Zum Seitenanfang

2.3.5StreamTokenizer * Zur vorigen ÜberschriftZur nächsten Überschrift

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.slashSlashComments( true ); // Kommentar
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()

URL url = new URL( "http://www.tutego.com/index.html" );
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 );
}
class java.io.StreamTokenizer
  • 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.

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java SE 8 Standard-Bibliothek Java SE 8 Standard-Bibliothek
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel
Java ist auch eine Insel


Zum Rheinwerk-Shop: Professionell entwickeln mit Java EE 8
Professionell entwickeln mit Java EE 8


Zum Rheinwerk-Shop: Besser coden
Besser coden


Zum Rheinwerk-Shop: Entwurfsmuster
Entwurfsmuster


Zum Rheinwerk-Shop: IT-Projektmanagement
IT-Projektmanagement


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2018
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern