Reader / Writer

Klassenhierarchie: Reader

Analog zu den Streams gibt es für das Lesen und Schreiben von Zeichenketten spezielle Klassen: Reader und Writer.

Ausgehend von einer abstrakten Basisklasse Reader gibt es für unterschiedlichen Speicherorte und Zwecke spezielle Subklassen. Diese sind:

  • StringReader – aus Strings lesen
  • CharacterArrayReader – aus char-Arrays lesen
  • BufferedReader – gepuffertes Lesen
  • PipedReader – Verbindung von Threads
  • InputStreamReader – Verknüpfung von Stream und Reader
  • FileReader – aus Dateien lesen
  • FilterReader – Filter
  • PushbackReader – lesen und zurückstellen von Zeichen

Methoden von Reader

  • int read() – liest ein Zeichen
  • int read(char[] cbuf) – liest Zeichen in das char-Array
  • int read(char[] cbuf, int off, int len) – liest maximal len Zeichen in das char-Array cbuf beginnend bei Position off
  • long skip(long n) – überspringt n Zeichen
  • boolean ready() – gibt an, ob der nächste read() auf dem Reader blockiert oder nicht
  • void mark(int readlimit) – markiert die aktuelle Position im Reader und vergisst sie erst nach readlimit Bytes die daraufhin gelesen wurden
  • void reset() – springt zu der mit mark() markierten Stelle im Reader zurück
  • boolean markSupported() – zeigt an, ob der Reader überhaupt das setzen einer Marke mit mark() unterstützt
  • void close() – schließt den Reader

Die Methoden des Reader entsprechen weitgehend denen der Streams, nur mit dem Unterschied, dass die Methoden char liefern und nehmen anstatt byte.

Auch hier ist wieder die Besonderheit zu beachten, dass die read-Methode den Rückgabetyp int hat, damit sie über den int-Wert -1 das Ende der Daten signalisieren kann. Deshalb darf man den Wert erst nach dem Vergleich mit -1 auf char casten.

Zeichen von einem Reader lesen

Reader fr = new FileReader("/tmp/test.txt");

int daten;

while ((daten = fr.read()) > -1) {
    char c = (char) daten;
    // jetzt kann man etwas sinnvolles mit den Zeichen machen,
    // die aus der Datei gelesen wurden
}

fr.close();
Reader fr = new FileReader("/tmp/test.txt");

char b;

while ((b = (char) fr.read()) > -1) { // FEHLER!!
    // jetzt kann man etwas sinnvolles mit den Zeichen machen,
    // die aus der Datei gelesen wurden
}

fr.close();

Dieser Code funktioniert nicht. Die Methode read() signalisiert das Ende der Daten durch einen Rückgabewert von -1 als int. Wenn man das Ergebnis aber vorher auf char castet, wird das Lesen niemals beendet, da char ein vorzeichenloser Datentyp ist und daher das Ergebnis eines Casts auf char immer >= 0 ist.

Blöcke von einem Reader lesen

Reader fr = new FileReader("/tmp/test.txt");

char[] daten = new char[1024];
int charactersRead;

while ((charactersRead = fr.read(daten)) > -1) {
    // jetzt kann man etwas sinnvolles mit den Zeichen machen,
    // die aus der Datei gelesen wurden
}

fr.close();

Beispiel: StringReader

String text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n" +
        "Nulla laoreet, sem vel mollis imperdiet, sapien mauris\n" +
        "sollicitudin arcu, sed viverra nulla dui at est.\n" +
        "Nunc est erat, semper id sollicitudin ut, pretium ac eros.\n";

Reader sr = new StringReader(text);
int gelesen;
while ((gelesen = sr.read()) > -1) {
    System.out.print((char) gelesen);
}

sr.close();
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Nulla laoreet, sem vel mollis imperdiet, sapien mauris
sollicitudin arcu, sed viverra nulla dui at est.
Nunc est erat, semper id sollicitudin ut, pretium ac eros.

Anstatt die Daten aus einer echten Quelle lesen, kann man mithilfe des StringReaders einen String als Quelle nehmen. Dies ist besonders angenehm für Tests, bei denen man auf so einfach die Funktionsweise z. B. eines Filter-Readers testen kann ohne Dateien anlegen zu müssen.

BufferedReader

BufferedReader

  • BufferedReader(Reader in)
  • BufferedReader(Reader in, int bufferSize)
  • String readLine()

Zu beachten ist, dass der BufferedReader die sehr nützliche readLine()-Methode einführt, mit der man Textdateien zeilenweise lesen kann. Das Ende der Datei wird bei dieser Methode durch den Rückgabewert null signalisiert.

Beispiel: BufferedReader

String dateiname = "/tmp/text.txt";
BufferedReader reader = new BufferedReader(
        new FileReader(dateiname));

String zeile;
int nummer = 1;
while ((zeile = reader.readLine()) != null) {
    System.out.printf("%03d %s%n", nummer, zeile);
    nummer++;
}

reader.close();
001 Lorem ipsum dolor sit amet, consectetur adipiscing elit.
002 Nulla laoreet, sem vel mollis imperdiet, sapien mauris
003 sollicitudin arcu, sed viverra nulla dui at est.
004 Nunc est erat, semper id sollicitudin ut, pretium ac eros.

Beispiel: Eigener FilterReader

public class UpperCaseReader extends FilterReader {

    public UpperCaseReader(Reader in) {
        super(in);
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {

        int result = super.read(cbuf, off, len);

        for (int i = off; i < off + result; i++) {
            cbuf[i] = Character.toUpperCase(cbuf[i]);
        }

        return result;
    }
}
String dateiname = "/tmp/text.txt";
BufferedReader reader = new BufferedReader(
        new UpperCaseReader(
                new FileReader(dateiname)));

String zeile;

while ((zeile = reader.readLine()) != null) {
    System.out.println(zeile);
}

reader.close();
LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT.
NULLA LAOREET, SEM VEL MOLLIS IMPERDIET, SAPIEN MAURIS
SOLLICITUDIN ARCU, SED VIVERRA NULLA DUI AT EST.
NUNC EST ERAT, SEMPER ID SOLLICITUDIN UT, PRETIUM AC EROS.

Klassenhierarchie: Writer

Analog zum OutputStream gibt es für das Schreiben von Zeichendaten die Subklassen von Writer. Diese sind:

  • StringWriter – In Strings schreiben
  • CharacterArrayWriter – In char-Arrays schreiben
  • BufferedWriter – Gepuffertes Schreiben
  • PipedWriter – Verbindung von Threads
  • OutputStreamWriter – Verbindung von Stream und Writer
  • FileWriter – In Datei schreiben
  • PrintWriter – Formatierte Ausgabe von Datentypen
  • FilterWriter – Filter

Die Methoden von Writer

  • void write(int c) – schreibt ein Zeichen
  • void write(char[] cbuf) – schreibt alle Zeichen aus dem char-Array
  • void write(char[] cbuf, int off, int len) – schreibt len Zeichen aus dem char-Array cbuf beginnend bei off
  • void write(String str) – schreibt den String str
  • void write(String str, int off, int len) – schreibt len Zeichen aus dem String str, beginnend bei off
  • Writer append(char c) – schreibt ein Zeichen
  • void flush() – leert interne Puffer und bringt die Daten auf die Platte
  • void close() – schließt den Writer (impliziert einen flush())

Zeichen in einen Writer schreiben

Writer fw = new FileWriter("/tmp/mytext");

fw.write('T');
fw.write('e');
fw.write('x');
fw.write('t');
fw.write('\n');

fw.close();
> cat /tmp/mytext
Text

Strings in einen Writer schreiben

Writer fw = new FileWriter("/tmp/mytext");

String daten = "Dies ist ein Text";

fw.write(daten);
fw.write(daten, 0, 5);
fw.write(daten, 0, 5);
fw.write(daten, 12, 5);
fw.write(daten, 12, 5);

fw.write("\n");

fw.close();
> cat /tmp/mytext
Dies ist ein TextDies Dies  Text Text

BufferedWriter

BufferedWriter

  • BufferedWriter(Writer out)
  • BufferedWriter(Writer out, int bufferSize)
  • void newLine()

Beispiel: CharArrayWriter

CharArrayWriter cw = new CharArrayWriter();
PrintWriter pw = new PrintWriter(cw);

pw.printf("<html><head><title>%s</title></head>%n", "Toller Titel");
pw.printf("<body>%s</body></html>", "Ganz viel Body");

pw.close();

String webseite = cw.toString();

System.out.println(webseite);
<html><head><title>Toller Titel</title></head>
<body>Ganz viel Body</body></html>

InputStreamReader etc.

  • Manche APIs liefern nur Streams, manchmal möchte man die Daten aber mit einem Reader/Writer verarbeiten
    • Sockets
    • Servlet API
  • InputStreamReader und OutputStreamWriter dienen dazu, Streams und Reader/Writer miteinander zu verbinden
  • Verwendung als Filter bzw. Decorator

Beispiel: Einfacher HTTP-Client

Socket socket = new Socket("www.web.de", 80);
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
Writer writer = new OutputStreamWriter(os);

writer.write("GET / HTTP/1.0\r\n\r\n");
writer.flush();

String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}

reader.close();
writer.close();
socket.close();
HTTP/1.1 200 OK
Date: Sun, 13 Sep 2010 08:47:59 GMT
Server: Apache/2
Last-Modified: Sun, 13 Sep 2010 08:45:25 GMT
ETag: "133970-18cc3-9202d340"
Accept-Ranges: bytes
Content-Length: 101571
Cache-Control: max-age=0
Expires: Sun, 13 Sep 2010 08:47:59 GMT
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de"
id="buster">
<head>
    <title>WEB.DE - E-Mail - Suche - DSL - Modem - Shopping
...

Die Klasse PrintWriter

  • Hat print(...) und println(...) Methoden für alle primitiven Datentypen, char[], String und Object
  • Die print-Methoden werfen keine IOExceptions, Fehler müssen mit checkError abgeholt werden
  • Bietet mit printf(...) eine mächtige Methode zur formatierten Ausgabe von Daten

Copyright © 2025 Thomas Smits