Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5 (Tiger-Release)
Buch: Java ist auch eine Insel
gp Kapitel 12 Datenströme und Dateien
  gp 12.1 Datei und Verzeichnis
    gp 12.1.1 Dateien und Verzeichnisse mit der Klasse File
    gp 12.1.2 Dateieigenschaften und -attribute
    gp 12.1.3 Dateien berühren, neue Dateien anlegen
    gp 12.1.4 Umbenennen und Verzeichnisse anlegen
    gp 12.1.5 Die Wurzel aller Verzeichnisse/Laufwerke
    gp 12.1.6 Verzeichnisse listen und Dateien filtern
    gp 12.1.7 Dateien und Verzeichnisse löschen
    gp 12.1.8 Implementierungsmöglichkeiten für die Klasse File
    gp 12.1.9 Verzeichnisse nach Dateien rekursiv durchsuchen
    gp 12.1.10 Sicherheitsprüfung
    gp 12.1.11 Namen der Laufwerke
    gp 12.1.12 Locking
  gp 12.2 Dateien mit wahlfreiem Zugriff
    gp 12.2.1 Ein RandomAccessFile öffnen
    gp 12.2.2 Aus dem RandomAccessFile lesen
    gp 12.2.3 Schreiben
    gp 12.2.4 Die Länge des RandomAccessFile
    gp 12.2.5 Hin und her in der Datei
  gp 12.3 Stream-Klassen und Reader/Writer
    gp 12.3.1 Die abstrakten Basisklassen
    gp 12.3.2 Übersicht über Ein-/Ausgabeklassen
  gp 12.4 Binäre Ein-/Ausgabe-Klassen InputStream und OutputStream
    gp 12.4.1 Die Klasse OutputStream
    gp 12.4.2 Ein Datenschlucker
    gp 12.4.3 Anwendung der Klasse FileOutputStream
    gp 12.4.4 Die Eingabeklasse InputStream
    gp 12.4.5 Anwenden der Klasse FileInputStream
    gp 12.4.6 Kopieren von Dateien
  gp 12.5 Daten filtern durch FilterInputStream und FilterOutputStream
    gp 12.5.1 Der besondere Filter PrintStream
    gp 12.5.2 Die Schnittstelle Appendable
    gp 12.5.3 System.in und System.out
  gp 12.6 Besondere OutputStream- und InputStream-Klassen
    gp 12.6.1 Bytes in den Strom schreiben mit ByteArrayOutputStream
    gp 12.6.2 Bytes in den Strom schreiben mit ByteArrayInputStream
    gp 12.6.3 Ströme zusammensetzen mit SequenceInputStream
  gp 12.7 Ressourcen wie Grafiken aus dem Klassenpfad und aus Jar-Archiven laden
  gp 12.8 Die Unterklassen von Writer
    gp 12.8.1 Die abstrakte Klasse Writer
    gp 12.8.2 Datenkonvertierung durch den OutputStreamWriter
    gp 12.8.3 In Dateien schreiben mit der Klasse FileWriter
    gp 12.8.4 StringWriter und CharArrayWriter
    gp 12.8.5 Writer als Filter verketten
    gp 12.8.6 Gepufferte Ausgabe durch BufferedWriter
    gp 12.8.7 Ausgabemöglichkeiten durch PrintWriter erweitern
    gp 12.8.8 Daten mit FilterWriter filtern
  gp 12.9 Die Klassen um Reader
    gp 12.9.1 Die abstrakte Basisklasse Reader
    gp 12.9.2 Automatische Konvertierungen mit dem InputStreamReader
    gp 12.9.3 Dateien lesen mit der Klasse FileReader
    gp 12.9.4 StringReader und CharArrayReader
  gp 12.10 Die Filter für Zeichenströme
    gp 12.10.1 Gepufferte Eingaben mit der Klasse BufferedReader
    gp 12.10.2 LineNumberReader zählt automatisch Zeilen mit
    gp 12.10.3 Eingaben filtern mit der Klasse FilterReader
    gp 12.10.4 Daten mit der Klasse PushbackReader zurücklegen
  gp 12.11 Kommunikation zwischen Threads mit Pipes
    gp 12.11.1 PipedOutputStream und PipedInputStream
    gp 12.11.2 PipedWriter und PipedReader
  gp 12.12 Datenkompression
    gp 12.12.1 Die Java-Unterstützung beim Komprimieren und Zusammenpacken
    gp 12.12.2 Datenströme komprimieren
    gp 12.12.3 Zip-Archive
    gp 12.12.4 Jar-Archive
  gp 12.13 Prüfsummen
    gp 12.13.1 Die Schnittstelle Checksum
    gp 12.13.2 Die Klasse CRC32
    gp 12.13.3 Die Adler32-Klasse
  gp 12.14 Persistente Objekte und Serialisierung
    gp 12.14.1 Objekte speichern mit der Standard-Serialisierung
    gp 12.14.2 Objekte über die Standard-Serialisierung lesen
    gp 12.14.3 Die Schnittstelle Serializable
    gp 12.14.4 Nicht serialisierbare Attribute mit transient aussparen
    gp 12.14.5 Das Abspeichern selbst in die Hand nehmen
    gp 12.14.6 Tiefe Objektkopien
    gp 12.14.7 Versionenverwaltung und die SUID
    gp 12.14.8 Wie die ArrayList serialisiert
    gp 12.14.9 Serialisieren in XML-Dateien
    gp 12.14.10 XML-API von Sun
  gp 12.15 Zugriff auf SMB-Server
    gp 12.15.1 jCIFS
  gp 12.16 Tokenizer
    gp 12.16.1 StreamTokenizer
    gp 12.16.2 CSV (Comma Separated Values)-Dateien verarbeiten
  gp 12.17 Die Logging-API


Galileo Computing

12.16 Tokenizedowntop

Zu den schon im String-Kapitel vorgestellen Tokenizern Scanner, StringTokenizer und split() aus String, gibt es im io-Paket eine weitere Klasse StreamTokenizer. Zusätzlich soll eine externe Klasse vorgestellt werden, die mit CSV-Dateien umgehen kann.


Galileo Computing

12.16.1 StreamTokenizer  downtop

Die Klasse StreamTokenizer aus dem 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 werden bestimmte Merkmale aus dem Text erkannt, so unter anderem Bezeichner (etwa Schlüsselworte), Zahlen, Strings in Anführungszeichen und verschiedene Kommentararten (C-Stil oder C++-Stil). Verschiedene Java-Tools von Sun 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, Hochkomma- beziehungsweise 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 beziehungsweise 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.


Beispiel   Die folgende Klasse liest die Eingabe aus einer Datei und gibt die erkannten Textteile aus:

Listing 12.42   StreamTokenizerDemo.java


import java.io.*;

class StreamTokenizerDemo
{
  public static void main( String args[] ) throws IOException
  {
    String fn = "StreamTokenizerDemo.java";

    StreamTokenizer st = new StreamTokenizer(
                           new FileReader(fn) );

//     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 );
    }
  }
}

Die Ausgabe des Programms beginnt wie folgt:


Zeichen: /
Zeichen: /
Wort: Version
Nummer: 1.1
Ende der Zeile
Ende der Zeile
Wort: import
Wort: java.io.
Zeichen: *
Zeichen: ;
Ende der Zeile
Ende der Zeile
Wort: class
Wort: StreamTokenizerDemo
Ende der Zeile
Zeichen: {
Ende der Zeile
Wort: public

class java.io.StreamTokenizer

gp  StreamTokenizer( Reader r )
Erzeugt einen Tokenizer, der den Datenstrom zerlegt. Der Konstruktor, der das Ganze auch mit einem InputStream macht, ist veraltet.
gp  void resetSyntax()
Reinitialisiert die Syntaxtabelle des Tokenizers, so dass kein Zeichen eine Sonderbehandlung genießt. Mit ordinaryChar() lässt sich das Verhalten eines Zeichen bestimmen.
gp  void wordChars( int low, int hi )
Zeichen im Bereich von low <= c <= high werden als Bestandteile von Wörtern erkannt, dementsprechend zusammengefasst und als Word-Token übergeben.
gp  void whitespaceChars( int low, int hi )
Zeichen im Bereich von low <= c <= high werden als Trennzeichen erkannt.
gp  void ordinaryChars( int low, int hi )
Zeichen im Bereich von low <= c <= high genießen keine Sonderbehandlung und werden als normale Zeichen einzeln behandelt.
gp  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).
gp  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, ., – als numerisch. Liegt eine Ganz- oder Fließkommazahl vor, so wird der Zahlenwert in nval abgelegt und das Token ergibt im Attribut ttype den Wert TT_NUMBER.
gp  void commentChar( int ch )
Gibt das Zeichen an, welches einen einzeiligen Kommentar einleitet. Alle nachfolgenden Zeichen werden dann bis zum Zeilenende ignoriert. So ließen sich beispielsweise FORTRAN-Kommentare nach commentChar( ’C’ ) überlesen.
gp  void slashStarComments( boolean flag )
Der Tokenizer soll Kommentare im C-Stil (/* Müll */) erkennen oder nicht.
gp  void slashSlashComments( boolean flag )
Der Tokenizer soll Kommentare im C++-Stil (// Zeile ) erkennen oder nicht.
gp  void lowerCaseMode( boolean fl )
Liegt in ttype ein Token vom Typ TT_WORD vor, so wird dies automatisch in Kleinschreibweise konvertiert, falls fl gleich true ist.
gp  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.
gp  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.
gp  int lineno()
Liefert die aktuelle Zeilennummer in der Eingabedatei.

Konsoleneingaben über StreamTokenizer einlesen

Da der StreamTokenizer mit nextToken() Zeichenketten liefert, können wir ihn mit dem Standardeingabestrom des Betriebssystems System.in initialisieren und anschließend von der Konsole in dem Attribut in.sval eingegebene Strings auslesen. So lassen sich Eingaben einfach verarbeiten.


StreamTokenizer in =   new StreamTokenizer( System.in );  
System.out.print( "Wie heißt du? ");
in.  nextToken();  
System.out.println( "Hallo " + in.sval );

Erweitern und Schwächen

Obwohl die nextToken()-Funktion eine ganze Menge an 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 Funktion nextToken() zu überschreiben und vielleicht auch noch toString(). Die Erweiterung von nextToken() erfordert jedoch etwas Arbeit, da das Original ein wenig unübersichtlich ist.


Galileo Computing

12.16.2 CSV (Comma Separated Values)-Dateien verarbeiten  toptop

Eine CSV-Datei bildet die Zeilen und Spalten einer Tabelle in einer ASCII-Datei ab. Die Zellen sind dabei durch einem Komma oder einem anderen Trennzeichen separiert. Texte können in Anführungszeichen gesetzt werden, um etwa Leerzeichen zu berücksichtigen.


Rodney,King,"Fahrer"
Bryant,Allen,"Gast auf dem Rücksitz"

Auch MS Excel kann Tabellen in das CSV-Format exportieren, nutzt aber in der deutschen Version als Trenner ein Semikolon – CSV wird bei MS also zu einer sprachabhängigen Datei. (Außer, der Export wird über ein englischsprachiges Makro angestoßen. Da ist es wieder ein Komma.) In der ersten Zeile stehen die Tabellenköpfe.

Sollten CSV-Dateien in Java verarbeitet werden, fällt spontan die Klasse StringTokenizer auf. Doch sie ist zum Einlesen nicht besonders gut geeignet. Welches Trennsymbol sollte gewählt werden? Sicherlich das Semikolon. Doch was ist, wenn dieses im Text vorkommt. Dann wird der Text in zwei Tokens aufgeteilt, was falsch ist. Des Weiteren kann der Java StringTokenizer nicht mit Leerstrings umgehen, also auf Zeilenfolgen wie ;; im Datenstrom reagieren; er würde sie überlesen, aber nicht "" zurückgeben.

StringTokenizer hilft hier nicht weiter, aber eine Bibliothek von Stephen Ostermiller unter http://ostermiller.org/utils/CSV.html. Mit dem CSVParser lassen sich leicht CSV-Dateien einlesen, und er behandelt auch Fluchtsymbole korrekt.


CSVParser csvParser = new CSVParser( FileInputStream("datei.csv") );
for ( String t; (t = csvParser.nextValue()) != null; )
  System.out.println( csvParser.lastLineNumber() + " " + t );




Copyright © Galileo Press GmbH 2004
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.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de