Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 18 - Character-Streams

## 18.3 Eingabe-Streams

- 18.3 Eingabe-Streams
  - 18.3.1 Die abstrakte Klasse Reader
  - 18.3.2 Auswahl des Eingabegerätes
    - InputStreamReader und FileReader
    - StringReader und CharArrayReader
  - 18.3.3 Schachteln von Eingabe-Streams
    - BufferedReader
    - LineNumberReader
    - FilterReader und PushbackReader

### 18.3.1 Die abstrakte Klasse Reader

Basis aller sequentiellen Eingaben ist die abstrakte Klasse `Reader`, die eine Schnittstelle für stream-basierte Eingaben zur Verfügung stellt:

    public Reader()

    public void close()
    public void mark(int readAheadlimit)
    public boolean markSupported()

    public int read()
    public int read(char[] cbuf)
    public int read(char[] cbuf, int off, int len)

    public long skip(long n)
    public boolean ready()
    public void reset()
| java.io.Reader

Analog zu den `write`-Methoden von `Writer` stellt die Klasse `Reader` eine Reihe von `read`-Methoden zum Lesen von Daten zur Verfügung. Die parameterlose Variante liest das nächste Zeichen aus dem Eingabestrom und liefert es als `int`, dessen Wert im Bereich von 0 bis 16383 liegt. Ein Rückgabewert von -1 zeigt das Ende des Eingabestroms an. Die beiden anderen Varianten von `read` übertragen eine Reihe von Zeichen in das als Parameter übergebene Array und liefern die Anzahl der tatsächlich gelesenen Zeichen als Rückgabewert. Auch hier wird -1 zurückgegeben, wenn das Ende des Streams erreicht ist.

Die Methode `ready` liefert `true`, falls der nächste Aufruf von `read` erfolgen kann, ohne daß die Eingabe blockt, und `close` schließt den Eingabestrom. Mit Hilfe der Methoden `mark` und `reset` gibt es die Möglichkeit, eine bestimmte Position innerhalb des Eingabe-Streams zu markieren und zu einem späteren Zeitpunkt wieder anzuspringen. Zuvor muß allerdings durch einen Aufruf von `markSupported` überprüft werden, ob das Markieren überhaupt unterstützt wird. Ist dies nicht der Fall, würde ein Aufruf von `mark` oder `reset` eine Ausnahme erzeugen. Die Methode `mark` merkt sich die aktuelle Leseposition und spezifiziert, wie viele Bytes anschließend maximal gelesen werden können, bevor die Markierung ungültig wird. Ein Aufruf von `reset` setzt den Lesezeiger an die markierte Stelle zurück.

Mit der Methode `skip` ist es möglich, n Zeichen im Eingabestrom zu überspringen. Dabei kann es aus verschiedenen Gründen vorkommen, daß nicht exakt die angegebene Anzahl an Zeichen übersprungen wird (beispielsweise, wenn nicht mehr genügend Zeichen vorhanden sind). Der Rückgabewert von `skip` gibt die tatsächliche Anzahl an.

### 18.3.2 Auswahl des Eingabegerätes

Analog zur Klasse `Writer` dient auch bei der Klasse `Reader` die erste Ebene abgeleiteter Klassen vorwiegend dazu, die Art des Datenlieferanten zu bestimmen. Tabelle 18.2 gibt eine Übersicht der aus `Reader` abgeleiteten Klassen.

Klasse | Bedeutung
`InputStreamReader` | Basisklasse für alle Reader, die einen Byte-Stream in einen Character-Stream umwandeln.
`FileReader` | Konkrete Ableitung von `InputStreamReader` zum Einlesen aus einer Datei.
`FilterReader` | Abstrakte Basisklasse für die Konstruktion von Eingabefiltern.
`PushbackReader` | Eingabefilter mit der Möglichkeit, Zeichen zurückzugeben.
`BufferedReader` | Reader zur Eingabepufferung und zum Lesen von kompletten Zeilen.
`LineNumberReader` | Ableitung aus `BufferedReader` mit der Fähigkeit, Zeilen zu zählen.
`StringReader` | Reader zum Einlesen von Zeichen aus einem `String`.
`CharArrayReader` | Reader zum Einlesen von Zeichen aus einem Zeichen-Array.
`PipedReader` | Reader zum Einlesen von Zeichen aus einem `PipedWriter`.

Tabelle 18.2: Aus Reader abgeleitete Klassen

In den folgenden Unterabschnitten werden die Klassen `InputStreamReader`, `FileReader`, `StringReader` und `CharArrayReader` erläutert. `FilterReader`, `PushbackReader`, `BufferedReader` und `LineNumberReader` werden in Abschnitt 18.3.3 behandelt.

|  | Die Klasse `PipedReader` soll hier nicht erläutert werden. In Abschnitt 22.4.4 findet sich jedoch ein Beispiel zur Anwendung der Klassen `PipedInputStream` und `PipedOutputStream`, das auf `PipedReader` und `PipedWriter` übertragbar ist. |  |
Hinweis

#### InputStreamReader und FileReader

Die Klasse `InputStreamReader` ist die Basisklasse für alle Reader, die eine Konvertierung zwischen Byte- und Character-Streams vornehmen. Sie enthält ein Objekt des Typs `ByteToCharConverter` (aus dem undokumentierten Paket `sun.io`), das die Konvertierung der Eingabezeichen bei allen lesenden Zugriffen vornimmt. Als übergeordnete Basisklasse ist `InputStreamReader` für uns allerdings nicht so interessant wie die daraus abgeleitete Klasse `FileReader`, die die Eingabe aus einer Datei ermöglicht. Sie implementiert die abstrakten Eigenschaften von `Reader` und bietet zusätzliche Konstruktoren, die es erlauben, eine Datei zu öffnen:

    public FileReader(String fileName)
      throws FileNotFoundException

    public FileReader(File file)
      throws FileNotFoundException

    public FileReader(FileDescriptor fd)
| java.io.FileReader

Bei der Übergabe der Zeichenkette `fileName` wird die Datei mit dem angegebenen Namen zum Lesen geöffnet. Falls sie nicht vorhanden ist, löst der Konstruktor eine Ausnahme des Typs `FileNotFoundException` aus. Die beiden anderen Konstruktoren erwarten ein `File`-Objekt, das eine zu öffnende Datei spezifiziert, oder ein `FileDescriptor`-Objekt, das eine bereits geöffnete Datei angibt.

Das folgende Beispiel demonstriert die Anwendung der Klasse `FileReader`. Das Programm liest die Datei config.sys und gibt ihren Inhalt auf dem Bildschirm aus:

    001 /* Listing1806.java */
    002
    003 import java.io.*;
    004
    005 public class Listing1806
    006 {
    007   public static void main(String[] args)
    008   {
    009     FileReader f;
    010     int c;
    011
    012     try {
    013       f = new FileReader("c:\\config.sys");
    014       while ((c = f.read()) != -1) {
    015          System.out.print((char)c);
    016       }
    017       f.close();
    018     } catch (IOException e) {
    019       System.out.println("Fehler beim Lesen der Datei");
    020     }
    021   }
    022 }
| Listing1806.java

Listing 18.6: Anwendung der Klasse FileReader

Die Ausgabe des Programms ist:

    DEVICE=c:\windows\himem.sys
    DOS=HIGH,UMB
    DEVICE=c:\windows\emm386.exe noems
    SHELL=c:\command.com/e:1536/p
    FILES=101
    BUFFERS=5
    DEVICEHIGH=c:\windows\command\ansi.sys

#### StringReader und CharArrayReader

Diese beide Klassen sind die Pendants zu `StringWriter` und `CharArrayWriter`. Sie erlauben das Lesen von Zeichen aus einem `String` bzw. einem Zeichen-Array. Die Schnittstelle der beiden Klassen ist identisch und unterscheidet sich von der Basisklasse `Reader` nur durch die geänderten Konstruktoren:

    public StringReader(String s)
| java.io.StringReader

    public CharArrayReader(char[] buf)

    public CharArrayReader(char[] buf, int offset, int length)
| java.io.CharArrayReader

Das folgende Programm zeigt die Verwendung der Klasse `StringReader` am Beispiel eines Programms, das einen `Reader` konstruiert, der den Satz liest, der hier an dieser Stelle steht:

    001 /* Listing1807.java */
    002
    003 import java.io.*;
    004
    005 public class Listing1807
    006 {
    007   public static void main(String[] args)
    008   {
    009     Reader f;
    010     int c;
    011     String s;
    012
    013     s =  "Das folgende Programm zeigt die Verwendung\r\n";
    014     s += "der Klasse StringReader am Beispiel eines\r\n";
    015     s += "Programms, das einen Reader konstruiert, der\r\n";
    016     s += "den Satz liest, der hier an dieser Stelle steht:\r\n";
    017     try {
    018       f = new StringReader(s);
    019       while ((c = f.read()) != -1) {
    020         System.out.print((char)c);
    021       }
    022       f.close();
    023     } catch (IOException e) {
    024       System.out.println("Fehler beim Lesen des Strings");
    025     }
    026   }
    027 }
| Listing1807.java

Listing 18.7: Verwendung der Klasse StringReader

Die Ausgabe des Programms ist:

    Das folgende Programm zeigt die Verwendung
    der Klasse StringReader am Beispiel eines
    Programms, das einen Reader konstruiert, der
    den Satz liest, der hier an dieser Stelle steht:

### 18.3.3 Schachteln von Eingabe-Streams

Das Konzept der geschachtelten Streams funktioniert bei der sequentiellen Eingabe genauso wie bei der Ausgabe. Mit den Klassen `BufferedReader`, `LineNumberReader`, `FilterReader` und `PushbackReader` stehen Klassen zur Verfügung, die im Konstruktor die Übergabe eines weiteren Readers erwarten und die Leseoperationen vor Ausführung der Filterfunktion an diesen `Reader` weiterleiten.

#### BufferedReader

Dieser Filter dient zur Pufferung von Eingaben und kann verwendet werden, um die Performance beim Lesen von externen Dateien zu erhöhen. Da nicht jedes Byte einzeln gelesen wird, verringert sich die Anzahl der Zugriffe auf den externen Datenträger, und die Lesegeschwindigkeit erhöht sich. Zusätzlich stellt `BufferedReader` die Methode `readLine` zur Verfügung, die eine komplette Textzeile liest und als `String` an den Aufrufer zurückgibt:

    public String readLine()
      throws IOException
| java.io.BufferedReader

Eine Textzeile wird dabei durch die Zeichen `'\n'` oder `'\r'` oder durch die Folge `"\r\n"` begrenzt. Der Rückgabewert von `readLine` ist ein `String`, der den Zeileninhalt ohne Begrenzungszeichen enthält bzw. `null`, falls das Ende des Streams erreicht ist. `BufferedReader` besitzt zwei Konstruktoren:

    public BufferedReader(Reader in)

    public BufferedReader(Reader in, int sz)
| java.io.BufferedReader

Der erste Parameter `in` ist das `Reader`-Objekt, auf dem der `BufferedReader` aufgesetzt werden soll. Der optionale zweite Parameter `sz` gibt die Größe des internen Puffers an. Fehlt er, so wird eine für die meisten Situationen angemessene Standardeinstellung verwendet.

Das folgende Beispiel demonstriert den Einsatz der Eingabepufferung durch die Erweiterung von Listing 18.6. Auch hier wird die Datei config.sys eingelesen und auf dem Bildschirm ausgegeben. Durch den Einsatz der Klasse `BufferedReader` versucht das Programm, die Perfomance beim Lesen der Datei zu erhöhen:

    001 /* Listing1808.java */
    002
    003 import java.io.*;
    004
    005 public class Listing1808
    006 {
    007   public static void main(String[] args)
    008   {
    009     BufferedReader f;
    010     String line;
    011
    012     try {
    013       f = new BufferedReader(
    014           new FileReader("c:\\config.sys"));
    015       while ((line = f.readLine()) != null) {
    016         System.out.println(line);
    017       }
    018       f.close();
    019     } catch (IOException e) {
    020       System.out.println("Fehler beim Lesen der Datei");
    021     }
    022   }
    023 }
| Listing1808.java

Listing 18.8: Eingabepufferung beim Lesen aus Dateien

Zusätzlich wird die Eingabe nicht zeichen-, sondern mit Hilfe von `readLine` zeilenweise gelesen, was die Performance weiter erhöht. Die Ausgabe des Programms ist mit der von Listing 18.6 identisch.

#### LineNumberReader

Diese Klasse ist eine Ableitung von `BufferedReader`, die um die Fähigkeit erweitert wurde, die Anzahl der Eingabezeilen beim Einlesen zu zählen. Die Schnittstelle entspricht dabei exakt der von `BufferedReader`, erweitert um die Methoden `getLineNumber` und `setLineNumber`:

    public int getLineNumber()

    public void setLineNumber(int lineNumber)
| java.io.LineNumberReader

Mit `getLineNumber` wird der aktuelle Stand des Zeilenzählers abgefragt, mit `setLineNumber` kann er sogar verändert werden.

Das folgende Beispiel erweitert unsere bisherigen Programme zur Ausgabe der Datei config.sys in der Weise, daß nun jede einzelne Zeile mit vorangestellter Zeilennummer angezeigt wird. Dazu wird der `BufferedReader` durch einen `LineNumberReader` ersetzt und vor der Ausgabe jeder einzelnen Zeile zunächst die korrespondierende Zeilennummer ausgegeben:

    001 /* Listing1809.java */
    002
    003 import java.io.*;
    004
    005 public class Listing1809
    006 {
    007   public static void main(String[] args)
    008   {
    009     LineNumberReader f;
    010     String line;
    011
    012     try {
    013       f = new LineNumberReader(
    014           new FileReader("c:\\config.sys"));
    015       while ((line = f.readLine()) != null) {
    016         System.out.print(f.getLineNumber() + ": ");
    017         System.out.println(line);
    018       }
    019       f.close();
    020     } catch (IOException e) {
    021       System.out.println("Fehler beim Lesen der Datei");
    022     }
    023   }
    024 }
| Listing1809.java

Listing 18.9: Die Klasse LineNumberReader

Die Ausgabe des Programms ist nun:

    1: DEVICE=c:\windows\himem.sys
    2: DOS=HIGH,UMB
    3: DEVICE=c:\windows\emm386.exe noems
    4: SHELL=c:\command.com/e:1536/p
    5: FILES=101
    6: BUFFERS=5
    7: DEVICEHIGH=c:\windows\command\ansi.sys

#### FilterReader und PushbackReader

Das Schachteln von Eingabestreams funktioniert analog zum Schachteln von Ausgabestreams. Auch hier existiert eine abstrakte Basisklasse `FilterReader`, die den eigentlichen `Reader` im Konstruktor übergeben bekommt und als Membervariable speichert. Bei der Konstruktion eigener Eingabefilter kann analog zur Konstruktion von Ausgabefiltern vorgegangen werden.

Das JDK 1.1 enthält einen vordefinierten Eingabefilter `PushbackReader`, der aus `FilterReader` abgeleitet wurde. Ein `PushbackReader` erweitert die Klasse `FilterReader` um einen ein Byte großen Pushbackbuffer. Dieser erlaubt einer Anwendung, das zuletzt gelesene Zeichen wieder in den Eingabestrom zurückzuschieben. Der nächste Lesezugriff liest dann nicht das folgende Zeichen im Eingabestrom, sondern das gerade zurückgegebene Zeichen. Wahlweise kann beim Aufruf des Konstruktors die Größe des Pushbackbuffers angegeben werden:

    public PushbackReader(Reader in)

    public PushbackReader(Reader in, int size)
| java.io.PushbackReader

Ein `PushbackReader` kann beispielsweise angewendet werden, wenn eine Methode das nächste Eingabezeichen kennen muß, um zu entscheiden, welche Aktion als nächstes auszuführen ist. Falls die Methode selbst nicht für die Behandlung des Eingabezeichens zuständig ist, kann sie das Zeichen an den Eingabestrom zurückgeben, und eine andere Methode kann mit der Bearbeitung beauftragt werden. Derartige Techniken finden beispielsweise in Werkzeugen zur lexikalischen Analyse Anwendung, wie sie im Compilerbau verwendet werden.

Die Rückgabe eines Zeichens wird mit Hilfe der Methode `unread` durchgeführt. Diese steht in verschiedenen Varianten zur Verfügung und kann zur Rückgabe eines einzelnen Zeichens oder mehrerer Zeichen verwendet werden:

    public void unread(int c)
      throws IOException

    public void unread(char[] cbuf, int off, int len)
      throws IOException

    public void unread(char[] cbuf)
      throws IOException
| java.io.PushbackReader

Hierbei muß das oder die zurückzugebenden Zeichen als Parameter `unread` übergeben werden.

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de