Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 7. Auflage
 <<    <     >    >>   API  Kapitel 19 - Character-Streams

19.3 Eingabe-Streams



19.3.1 Die abstrakte Klasse Reader

Basis aller sequenziellen 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 65535 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 dass 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 muss 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, dass 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.

19.3.2 Auswahl des Eingabegeräts

Analog zur Klasse Writer dient auch bei der Klasse Reader die erste Ebene abgeleiteter Klassen vorwiegend dazu, die Art des Datenlieferanten zu bestimmen. Tabelle 19.2 enthält 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 19.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 19.3.3 behandelt.

Die Klasse PipedReader soll hier nicht erläutert werden. In Abschnitt 23.4.5 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 /etc/hosts auf einem Unix-System und gibt ihren Inhalt auf dem Bildschirm aus:

001 /* Listing1906.java */
002 
003 import java.io.*;
004 
005 public class Listing1906
006 {
007   public static void main(String[] args)
008   {
009     FileReader f;
010     int c;
011 
012     try {
013       f = new FileReader("/etc/hosts");
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 }
Listing1906.java
Listing 19.6: Anwendung der Klasse FileReader

Die Ausgabe des Programms ist:

127.0.0.1	localhost

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts

StringReader und CharArrayReader

Diese beiden 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 /* Listing1907.java */
002 
003 import java.io.*;
004 
005 public class Listing1907
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 }
Listing1907.java
Listing 19.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:

19.3.3 Schachteln von Eingabe-Streams

Das Konzept der geschachtelten Streams funktioniert bei der sequenziellen 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 Reader 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 19.6. Auch hier wird die Datei /etc/hosts 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 /* Listing1908.java */
002 
003 import java.io.*;
004 
005 public class Listing1908
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("/etc/hosts"));
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 }
Listing1908.java
Listing 19.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 19.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 /etc/hosts in der Weise, dass 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 /* Listing1909.java */
002 
003 import java.io.*;
004 
005 public class Listing1909
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("/etc/hosts"));
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 }
Listing1909.java
Listing 19.9: Die Klasse LineNumberReader

Die Ausgabe des Programms ist nun:

1: 127.0.0.1	localhost
2: 
3: # The following lines are desirable for IPv6 capable hosts
4: ::1     ip6-localhost ip6-loopback
5: fe00::0 ip6-localnet
6: ff00::0 ip6-mcastprefix
7: ff02::1 ip6-allnodes
8: ff02::2 ip6-allrouters
9: ff02::3 ip6-allhosts

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 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 muss, 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 Compiler-Bau 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 muss das oder die zurückzugebenden Zeichen als Parameter unread übergeben werden.


 Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 7. Auflage, Addison Wesley, Version 7.0
 <<    <     >    >>   API  © 1998, 2011 Guido Krüger & Heiko Hansen, http://www.javabuch.de