|  | 
 | |||||
|  |  | 
 
 
 
 
 Listing 12.22 LineNumberReaderDemo.java import java.io.*; public class LineNumberReaderDemo { public static void main( String args[] ) { try { Reader fr = new FileReader( "LineNumberReaderDemo.java" ); LineNumberReader f = new LineNumberReader( fr ); for ( String line; (line = f.readLine()) != null; ) System.out.println( f.getLineNumber() + ": " + line ); f.close(); } catch ( IOException e ) { System.out.println( "Fehler beim Lesen der Datei" ); } } } 12.8.3 Eingaben filtern mit der Klasse FilterReader | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abstract class java.io.FilterReader extends Reader | 
|  | protected Reader in Der Zeicheneingabestrom oder null, wenn der Strom geschlossen wurde. | 
|  | protected FilterReader( Reader in ) Erzeugt einen neuen filternden Reader. | 
Die Methoden read(), read(char[] cbuf, int off, int len), skip(long n), ready(), markSupported(), mark(int readAheadLimit), reset() und close() werden überschrieben und leiten die Aufrufe direkt an Reader weiter. Wenn dieser eine Exception wirft, wird sie an uns weitergeleitet.
Unser nächstes Beispiel ist eine Klasse, die den FilterReader so erweitert, dass HTML-Tags überlesen werden. Sie werden allerdings nicht so komfortabel wie beim HTMLWriter im Datenstrom umgesetzt. Die Klasse überschreibt den notwendigen Konstruktor und implementiert die beiden read()-Methoden. Die read()-Methode ohne Parameter legt einfach ein ein Zeichen großes Feld an und ruft dann die read()-Methode auf, die die Daten in ein Feld liest. Da dieser Methode neben dem Feld auch noch die Größe übergeben werden kann, müssen wirklich so viele Zeichen gelesen werden. Es reicht einfach nicht aus, die übergebene Anzahl von Zeichen vom Reader in zu lesen, sondern hier müssen wir beachten, dass eingestreute Tags nicht zählen. Die Zeichenkette <p>Hallo<p> ist demnach fünf Zeichen lang und nicht elf. Liegt eine solche Zeichenkette vor, so müssen mehr als vier Zeichen vom darunter liegenden Reader abgenommen werden.
Listing 12.23 HTMLReader.java
import java.io.*; class HTMLReader extends FilterReader { public HTMLReader( Reader in ) { super( in ); } public int read() throws IOException { char buf[] = new char[1]; return read(buf, 0, 1) == -1 ? -1 : buf[0]; } public int read( char[] cbuf, int off, int len ) throws IOException { int numchars = 0; while ( numchars == 0 ) { numchars = in.read( cbuf, off, len ); if ( numchars == -1 ) // EOF? return -1; int last = off; for( int i = off; i < off + numchars; i++ ) { if ( !intag ) { if ( cbuf[i] == '<' ) intag = true; else cbuf[last++] = cbuf[i]; } else if (cbuf[i] == '>') intag = false; } numchars = last - off; } return numchars; } private boolean intag = false; } public class HTMLReaderDemo { public static void main( String args[] ) { try { String s = "<html>Hallo! <b>Ganz schön fett.</b>"+ "Ah, wieder normal.</html>"; StringReader sr = new StringReader( s ); HTMLReader hr = new HTMLReader( sr ); BufferedReader in = new BufferedReader( hr ); String t; while ( (t = in.readLine()) != null ) System.out.println( t ); in.close(); } catch( Exception e ) { System.err.println( e ); } } }
Das Programm produziert dann die einfache Ausgabe:
Hallo! Ganz schön fett. Ah, wieder normal.
Der einzige Grund, warum wir auf den HTMLReader noch einen BufferedReader aufsetzen, ist der, dass wir dann die readLine()-Methode nutzen können.


Der Eingabefilter PushbackReader ist die einzige Klasse, die direkt aus FilterReader abgeleitet ist. Sie definiert eine Filter-Klasse, die einen Puffer einer beliebigen Größe besitzt, in den Zeichen wieder zurückgeschrieben werden können.
Schreiben wir einen Parser, der eine Wahl aufgrund des nächsten gelesenen Zeichens (ein so genannter Vorausschauender Parser) trifft, dann kann er dieses Zeichen wieder in den Eingabestrom legen, wenn er den Weg doch nicht verfolgen möchte. Hier ist der Einsatz der Klasse PushbackReader angebracht. Denn der nächste Lesezugriff liest dann dieses zurückgeschriebene Zeichen.

Hier klicken, um das Bild zu Vergrößern
| class java.io.PushbackReader extends FilterReader | 
|  | PushbackReader( Reader in ) Erzeugt einen PushbackReader aus dem Reader in mit der Puffergröße 1. | 
|  | PushbackReader( Reader in, int size ) Erzeugt einen PushbackReader aus dem Reader in mit der Puffergröße size. | 
Um ein Zeichen oder eine Zeichenfolge wieder in den Eingabestrom zu legen, wird die Methode unread() ausgeführt.
|  | 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 Legt ein Zeichen oder ein Feld von Zeichen zurück in den Zeichenstrom. | 
Das nächste Programm demonstriert die Möglichkeiten eines PushbackReaders. Die Implementierung wirkt möglicherweise etwas gezwungen, sie zeigt jedoch, wie unread() eingesetzt werden kann. Das Programm löst folgendes Problem: Wir haben eine Textdatei (im Programm einfach als String über einen StringReader zur Verfügung gestellt), in der Zeilennummern mit dem String verbunden sind.
134Erste Zeile
234Zeile
Wir wollen nun die Zahlen vom Rest der Zeilen trennen. Dazu lesen wir so lange die Zahlen ein, bis ein Zeichen folgt, bei dem Character.isDigit() die Rückgabe false ergibt. Dann wissen wir, dass wir keine Ziffer mehr im Strom haben. Das Problem ist nun, dass schon ein Zeichen mehr gelesen wurde. In einem normalen Programm ohne die Option, das Zeichen zurücklegen zu können, würde das etwas ungemütlich. Dieses Zeichen müsste dann gesondert behandelt werden, da es das erste Zeichen der neuen Eingabe ist und nicht mehr zur Zahl gehört. Doch an Stelle dieser Sonderbehandlung legen wir es einfach wieder mit unread() in den Datenstrom, und dann kann der nachfolgende Programmcode einfach so weitermachen, als ob nichts gewesen wäre. Dies ist besonders dann von Vorteil, wenn noch Unterprogramme im Einsatz sind, die nach dem Lesen der Zahl eine weitere Funktion aufrufen, die noch einmal alles lesen will. Nach der herkömmlichen Methode muss das gelesene Zeichen dann mit an die Funktion übergeben werden.
Listing 12.24 PushbackReaderDemo.java
import java.io.*; class PushbackReaderDemo { public static void main( String args[] ) throws IOException { String s = "134Erste Zeile\n234Zeile"; PushbackReader in = new PushbackReader(new StringReader(s)); boolean eof = false; int c; while ( !eof ) { try { int number = 0; // Lese Zahl bis nichts mehr geht while ( Character.isDigit((char)(c = in.read())) ) number = (number * 10) + (c-'0'); if ( c == -1 ) // Ende der Datei => Ende der Schleife { eof = true; continue; } else in.unread( c ); // Letztes Zeichen wieder rein System.out.print( number + ":" ); // Hier ist das Zeichen wieder drinne while ( (c = in.read()) != -1 ) { System.out.print( (char)c ); if ( c == '\n' ) break; } if ( c == -1 ) { // Ende der Schleife eof = true; continue; } } catch ( EOFException e ) { eof = true; } } } }
Da PushbackReader nicht von BufferedReader abgeleitet ist und auch selbst keine Methode readLine() anbietet, müssen wir mit einer kleinen Schleife selbst Zeilen lesen. Im Bedarfsfall muss die Zeichenkombination »\n\r« gelesen werden. So wie die Methode von uns jetzt programmiert ist, ist sie auf Unix-Plattformen eingeschränkt, die nur ein einziges Ende-Zeichen einfügen. Doch warum nutzen wir nicht readLine()? Wer nun auf die Idee kommt, folgende Zeilen zu schreiben, um doch in den Genuss der Methode readLine() zu kommen, ist natürlich auf dem Holzweg:
StringReader sr = new StringReader( s ); BufferedReader br = new BufferedReader ( sr ); PushbackReader in = new PushbackReader( br ); ... br.readLine(); // Achtung, br!!
Wenn wir dem PushbackReader das Zeichen wiedergeben, dann arbeitet der BufferedReader genau eine Ebene darüber und bekommt vom Zurückgeben nichts mit. Daher ist es sehr gefährlich, die Verkettung zu umgehen. Im konkreten Fall wird das unread() nicht durchgeführt, und das erste Zeichen nach der Zahl fehlt.
| << zurück | 
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.