Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 5. Auflage |
<< | < | > | >> | API | Kapitel 18 - Character-Streams |
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.
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. |
|
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 |
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
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 /* 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 |
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:
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 Readers erwarten und die Leseoperationen vor Ausführung der Filterfunktion an diesen Reader weiterleiten.
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 |
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.
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, 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 /* 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 |
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
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 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 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 muss das oder die zurückzugebenden Zeichen als Parameter unread übergeben werden.
Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 5. Auflage, Addison Wesley, Version 5.0.2 |
<< | < | > | >> | API | © 1998, 2007 Guido Krüger & Thomas Stark, http://www.javabuch.de |