Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
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 Sicherheitsprüfung
gp 12.1.4 Umbenennen und Verzeichnisse anlegen
gp 12.1.5 Die Wurzel aller Verzeichnisse
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.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 Übersicht über wichtige Stream- und WriterReader
gp 12.3.1 Die abstrakten Basisklassen
gp 12.3.2 Übersicht über Ein-/Ausgabeklassen
gp 12.4 Eingabe- und 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.4.7 Daten filtern durch FilterInputStream und FilterOutputStream
gp 12.4.8 Der besondere Filter PrintStream
gp 12.4.9 System.in und System.out
gp 12.4.10 Bytes in den Strom schreiben mit ByteArrayOutputStream
gp 12.4.11 Ströme zusammensetzen mit SequenceInputStream
gp 12.5 Ressourcen wie Grafiken aus dem Klassenpfad und aus Jar-Archiven laden
gp 12.6 Die Unterklassen von Writer
gp 12.6.1 Die abstrakte Klasse Writer
gp 12.6.2 Datenkonvertierung durch den OutputStreamWriter
gp 12.6.3 In Dateien schreiben mit der Klasse FileWriter
gp 12.6.4 StringWriter und CharArrayWriter
gp 12.6.5 Writer als Filter verketten
gp 12.6.6 Gepufferte Ausgabe durch BufferedWriter
gp 12.6.7 Ausgabemöglichkeiten durch PrintWriter erweitern
gp 12.6.8 Daten mit FilterWriter filtern
gp 12.7 Die Klassen um Reader
gp 12.7.1 Die abstrakte Basisklasse Reader
gp 12.7.2 Automatische Konvertierungen mit dem InputStreamReader
gp 12.7.3 Dateien lesen mit der Klasse FileReader
gp 12.7.4 StringReader und CharArrayReader
gp 12.8 Schachteln von Eingabe-Streams
gp 12.8.1 Gepufferte Eingaben mit der Klasse BufferedReader
gp 12.8.2 LineNumberReader zählt automatisch Zeilen mit
gp 12.8.3 Eingaben filtern mit der Klasse FilterReader
gp 12.8.4 Daten mit der Klasse PushbackReader zurücklegen
gp 12.9 Kommunikation zwischen Threads mit Pipes
gp 12.9.1 PipedOutputStream und PipedInputStream
gp 12.9.2 PipedWriter und PipedReader
gp 12.10 Datenkompression
gp 12.10.1 Die Java-Unterstützung beim Komprimieren und Zusammenpacken
gp 12.10.2 Datenströme komprimieren
gp 12.10.3 Zip-Archive
gp 12.11 Prüfsummen
gp 12.11.1 Die Schnittstelle Checksum
gp 12.11.2 Die Klasse CRC32
gp 12.11.3 Die Adler32-Klasse
gp 12.12 Persistente Objekte und Serialisierung
gp 12.12.1 Objekte speichern
gp 12.12.2 Objekte lesen
gp 12.12.3 Die Schnittstelle Serializable
gp 12.12.4 Nicht serialisierbare Attribute mit transient aussparen
gp 12.12.5 Das Abspeichern selbst in die Hand nehmen
gp 12.12.6 Tiefe Objektkopien
gp 12.12.7 Versionenverwaltung und die SUID
gp 12.12.8 Wie die ArrayList serialisiert
gp 12.12.9 Serialisieren in XML-Dateien
gp 12.12.10 JSX (Java Serialization to XML)
gp 12.12.11 XML-API von Sun
gp 12.13 Zugriff auf SMB-Server
gp 12.13.1 jCIFS
gp 12.14 Tokenizer
gp 12.14.1 StreamTokenizer
gp 12.14.2 CSV (Comma Separated Values)-Dateien verarbeiten
gp 12.15 Die Logging-API


Galileo Computing

12.7 Die Klassen um Readerdowntop

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.


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

Tabelle 12.5 Von Reader direkt abgeleitete Klassen

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.7.1 Die abstrakte Basisklasse Readerdowntop

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

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() 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 gibt an, 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.

Galileo Computing

12.7.2 Automatische Konvertierungen mit dem InputStreamReaderdowntop

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.7.3 Dateien lesen mit der Klasse FileReaderdowntop

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.7.4 StringReader und CharArrayReadertoptop

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