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.9 Die Klassen um Readedowntop

Die Basisklasse aller Unicode-Eingaben ist die abstrakte Klasse Reader, genauso wie InputStream die Basisklasse für Byte-orientierte Streams ist. Daraus leiten sich weitere Klassen ab, die sequenziellen Zugriff auf Daten erlauben. Wie bei den Writer-Objekten auch, beziehen sich die Unterklassen auf bestimmte Eingabegeräte mit bestimmtem Verhalten.

Die folgende Tabelle gibt einen Überblick über die Klassen und einen Vorgeschmack darauf, was in den folgenden Unterkapiteln beschrieben wird.


Tabelle 12.5   Von Reader direkt abgeleitete Klassen

Klasse Bedeutung
InputStreamReader Abstrakte Basisklasse für alle Reader, die einen Byte-Stream in einen Zeichen-Stream umwandeln
FilterReader Abstrakte Basisklasse für Eingabefilter
PushbackReader Eingabefilter, der Zeichen zurückgeben kann
BufferedReader Reader, der Zeilen puffert
StringReader Reader, der aus einem String liest
CharArrayReader Reader, der Zeichen-Arrays liest
PipedReader Reader, der aus einem PipedWriter liest

Mit dem FileReader lässt sich aus Dateien lesen. Die Klasse FileReader geht wie ein FileWriter nicht direkt aus der Klasse Reader hervor, sondern ein InputStreamReader sitzt hier noch dazwischen. Auch die Klasse LineNumberReader geht nicht direkt aus dem Reader hervor. Es ist eine Ableitung aus BufferedReader mit der Fähigkeit, Zeilen zu zählen.


Galileo Computing

12.9.1 Die abstrakte Basisklasse Reader  downtop

Die abstrakte Klasse Reader dient zum Lesen von Zeichen aus einem Eingabestrom. Die einzigen Methoden, die Unterklassen implementieren müssen, sind read(char[],int,int) und close(). Dies entspricht dem Vorgehen bei den Writer-Klassen, die auch nur close() und write(char[],int,int) implementieren müssen. Eine abstrakte flush()-Methode, wie sie Writer besitzt, kann Reader nicht haben. Es bleiben demnach für die Reader-Klasse zwei abstrakte Methoden übrig. Die Unterklassen implementieren jedoch auch andere Methoden aus Geschwindigkeitsgründen neu.



abstract class java.io.  Reader  
implements Readable, Closeable

gp  protected Reader()
Erzeugt einen neuen Reader, der sich mit sich selbst synchronisiert.
gp  protected Reader( Object lock )
Erzeugt einen neuen Reader, der mit dem Objekt lock synchronisiert ist.
gp  abstract int read( char cbuf[], int off, int len ) throws IOException
Liest len Zeichen in den Puffer cbuf ab der Stelle off. Wenn len Zeichen nicht vorhanden sind, wartet der Reader. Gibt die Anzahl gelesener Zeichen zurück oder –1, wenn das Ende des Stroms erreicht wurde.
gp  int read( CharBuffer target ) throws IOException
Liest Zeichen in den CharBuffer. Die Funktion gibt es seit Java 5 durch die Schnittstelle Readable.
gp  int read() throws IOException
Die parameterlose Methode liest das nächste Zeichen aus dem Eingabestrom. Die Methode wartet, wenn kein Zeichen im Strom bereitliegt. Der Rückgabewert ist ein int im Bereich 0 bis 65636 (0x0000–0xffff). Warum dann der Rückgabewert aber int und nicht char ist, kann leicht damit erklärt werden, dass die Methode den Rückgabewert –1 (0xffffffff) kodieren muss, falls keine Daten anliegen.
gp  int read( char cbuf[] ) throws IOException
Liest Zeichen aus dem Strom und schreibt sie in ein Feld. Die Methode wartet, bis Eingaben anliegen. Der Rückgabewert ist die Anzahl der gelesenen Zeichen oder –1, wenn das Ende des Datenstroms erreicht wurde.
gp  abstract void close() throws IOException
Schließt den Strom. Folgt anschließend noch ein Aufruf von read(), ready(), mark() oder reset(), lösen diese eine IOException aus. Ein doppelt geschlossener Stream hat keinen weiteren Effekt.

Neben diesen notwendigen Methoden, die bei der Klasse Reader gegeben sind, kommen noch weitere interessante Funktionen hinzu, die den Status abfragen und Positionen setzen lassen. Die Methode ready() liefert als Rückgabe true, wenn ein read() ohne Blockierung der Eingabe möglich ist. Die Implementierung von Reader gibt immer false zurück. Mit der Methode mark() lässt sich eine bestimmte Position innerhalb des Eingabestroms markieren. Die Methode sichert dabei die Position. Mit beliebigen reset()-Aufrufen lässt sich diese konkrete Stelle zu einem späteren Zeitpunkt wieder anspringen. mark() besitzt einen Ganzzahl-Parameter, der angibt, wie viele Zeichen gelesen werden dürfen, bevor die Markierung nicht mehr gültig ist. Die Zahl ist wichtig, da sie die interne Größe des Puffers bezeichnet, der für den Strom angelegt werden muss. Nicht jeder Datenstrom unterstützt dieses Hin- und Herspringen. Die Klasse StringReader unterstützt etwa die Markierung einer Position, die Klasse FileReader dagegen nicht. Daher sollte vorher mit markSupported() überprüft werden, ob das Markieren auch unterstützt wird. Wenn der Datenstrom es nicht unterstützt und wir diese Warnung ignorieren, werden wir eine IOException bekommen. Denn Reader implementiert mark() und read() ganz einfach und muss von uns im Bedarfsfall überschrieben werden.


public void mark(int readAheadLimit) throws IOException {
  throw new IOException("mark() not supported");
}
public void reset() throws IOException {
  throw new IOException("reset() not supported");
}

Daher gibt markSupported() auch in der Reader-Klasse false zurück.

gp  long skip( long n ) throws IOException
Überspringt n Zeichen. Blockt, bis Zeichen vorhanden sind. Gibt die Anzahl der wirklich übersprungenen Zeichen zurück.
gp  public boolean ready() throws IOException
true, wenn aus dem Stream direkt gelesen werden kann. Das heißt allerdings nicht, dass false immer Blocken bedeutet.
gp  boolean markSupported()
Der Stream unterstützt die mark()-Operation.
gp  void mark( int readAheadLimit ) throws IOException
Markiert eine Position im Stream. Der Parameter bestimmt, nach wie vielen Zeichen die Markierung ungültig wird, mit anderen Worten, er gibt die Puffergröße an.
gp  void reset() throws IOException
Falls eine Markierung existiert, setzt der Stream an der Markierung an. Wurde die Position vorher nicht gesetzt, dann wird eine IOException mit dem String »Stream not marked« geworfen.

Reader implementiert die schon bekannte Schnittstelle Closeable mit der Methode close(). Und so wie seit Java 5 ein Writer die Schnittstele Appendable implementiert, so implementiert ein Reader die Schnittstelle Readable und damit die Operation int read(CharBuffer target) throws IOException.


Galileo Computing

12.9.2 Automatische Konvertierungen mit dem InputStreamReader  downtop

Unsere Basisklasse für alle Reader, die eine Konvertierung zwischen Byte- und Zeichen-Streams vornehmen, ist InputStreamReader. Die Klasse ist nicht abstrakt und hat demnach auch keine abstrakten Methoden. Sie arbeitet wie ein OutputStreamWriter und konvertiert die Daten mit Hilfe eines sun.nio.cs.StreamDecoders. Da wir die Arbeitsweise schon an anderer Stelle beschrieben haben, verzichten wir nun darauf.



class java.io.  InputStreamReader  
extends Reader

gp  InputStreamReader( InputStream in )
Erzeugt einen InputStreamReader mit der Standardkodierung.
gp  InputStreamReader(InputStream in, String enc) throws UnsupportedEncodingException
Erzeugt einen InputStreamReader, der die angegebene Zeichenkodierung anwendet.
gp  String getEncoding()
Liefert einen String mit dem Namen der Kodierung zurück. Der Name ist kanonisch und kann sich daher von dem String, der im Konstruktor übergeben wurde, unterscheiden.
gp  int read() throws IOException
Liest ein einzelnes Zeichen oder gibt –1 zurück, falls der Stream am Ende ist.
gp  int read( char cbuf[], int off, int len ) throws IOException
Liest einen Teil eines Felds.
gp  boolean ready() throws IOException
Kann vom Stream gelesen werden. Ein InputStreamReader ist bereit, wenn der Eingabepuffer nicht leer ist oder Bytes des darunter befindlichen InputStreams anliegen.

Wie wir an dieser Stelle bemerken, unterstützt ein reiner InputStream kein mark() und reset(). Da FileReader die einzige Klasse in der Java-Bibliothek ist, die einen InputStreamReader erweitert, und diese Klasse ebenfalls kein mark() beziehungsweise reset() unterstützt, lässt sich sagen, dass kein InputStreamReader der Standardbibliothek Positionsmarkierungen erlaubt.

Vergleich Reader und InputStream

Ein InputStreamReader eignet sich gut für die Umwandlung von äußeren Bytequellen. Wir erinnern uns, dass Java 16-Bit-Unicode-Zeichen verwendet, aber viele Computersysteme nur mit 8-Bit-ASCII-Zeichen arbeiten. Wenn wir also ein einzelnes Zeichen lesen, muss die passende Konvertierung in das richtige Zeichenformat gesichert sein. Der einfachste Weg ist, ein Zeichen zu lesen und es in ein char – allerdings ohne Konvertierung – zu casten, beispielsweise wie folgt:


InputStream fis = new FileInputStream( "file.txt" );
DataInputStream dis = new DataInputStream( fis );
char c = (char) dis.readByte();

Da hier keine Konvertierung durchgeführt wird, ist dieser Weg nicht gut. Empfehlenswert ist die Verwendung eines InputStreamReader, der die acht Bit in ein Unicode-Zeichen portiert.


InputStream fis = new FileInputStream( "file.txt" );
InputStreamReader isr = new InputStreamReader( fis );
char c = (char) isr.read();

Im nächsten Punkt beschreiben wir die Klasse FileReader, die direkt die Datei öffnet und den FileInputStream für uns anlegt.


Galileo Computing

12.9.3 Dateien lesen mit der Klasse FileReader  downtop

Die Klasse FileReader geht direkt aus einem InputStreamReader hervor. Von der Klasse Writer ist bekannt, dass Konstruktoren hinzugefügt werden, damit die Datei geöffnet werden kann, so auch hier.



class java.io.  FileReader  
extends InputStreamReader

gp  public FileReader( String fileName ) throws FileNotFoundException
Öffnet die Datei über einen Dateinamen zum Lesen. Falls sie nicht vorhanden ist, löst der Konstruktor eine FileNotFoundException aus.
gp  public FileReader( File file ) throws FileNotFoundException
Öffnet die Datei zum Lesen über ein File-Objekt. Falls sie nicht vorhanden ist, löst der Konstruktor eine FileNotFoundException aus.
gp  public FileReader( FileDescriptor fd )
Nutzt die schon vorhandene offene Datei über ein FileDescriptor-Objekt.

Nachfolgend zeigen wir die Anwendung der FileReader-Klasse, die ihren eigenen Quellcode auf den Bildschirm ausgibt. Die Datei muss im korrekten Pfad sein.

Listing 12.19   FileReaderDemo.java


import java.io.*;

public class FileReaderDemo
{
  public static void main( String args[] )
  {
    try
    {
      FileReader f =   new FileReader  ( "FileReaderDemo.java" );

      for ( int c; ( c = f.read() ) !=1; )
        System.out.print( (char) c );

      f.close();
    }
    catch ( IOException e ) {
      System.out.println( "Fehler beim Lesen der Datei" );
    }
  }
}

Galileo Computing

12.9.4 StringReader und CharArrayReader  toptop

Die Klassen StringWriter und CharArrayWriter haben die entsprechenden Lese-Klassen mit den Namen StringReader und CharArrayReader. Beide erlauben das Lesen von Zeichen aus einem String beziehungsweise aus einem Zeichenfeld. Sie leiten sich beide direkt aus Writer ab.

Listing 12.20   StringReaderDemo.java


import java.io.*;

class StringReaderDemo
{
  public static void main( String args[] ) throws IOException
  {
    String s = "Hölle Hölle Hölle";
    StringReader sr =   new StringReader( s );  
    char H = (char) sr.read();
    char ö = (char) sr.read();

    for ( int c; (c = sr.read()) !=1; )
      System.out.print( (char) c );

    sr.close();
  }
}

Abbildung
Hier klicken, um das Bild zu Vergrößern



class java.io.  StringReader  
extends Reader

gp  StringReader( String s )
Erzeugt einen neuen StringReader, der die Zeichen aus dem String s liest.


class java.io.  CharArrayReader  
extends Reader

gp  CharArrayReader( char buf[] )
Erzeugt einen CharArrayReader vom angegebenen Feld.
gp  CharArrayReader( char buf[], int offset, int length )
Erzeugt einen CharArrayReader vom angegebenen Feld der Länge length und der angegebenen Verschiebung.

Die Klassen StringReader und CharArrayReader überschreiben die Funktionen close(), mark(int), markSupported(), read(), read(char[] cbuf, int off, int len), ready(), reset() und skip(long). Sie unterstützen skip() und mark() beziehungsweise reset().


Tipp   Das Zeichenfeld, das CharArrayReader erhält, wird intern nicht kopiert, sondern referenziert. Das heißt, dass nachträgliche Änderungen am Feld die aus dem Stream gelesenen Zeichen beeinflussen.





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