Ein- und Ausgabe über Streams
Ein- und Ausgabe über Streams
Programme müssen Daten von unterschiedlichen Datenquellen lesen können und sie müssen Daten auf unterschiedliche Datenziele schreiben können. Hierbei kann es sich um die unterschiedlichsten Quellen und Ziele handeln:
|
Alle Ein- und Ausgaben in Java laufen "stromorientiert" ab, sie folgen dem "Stream-Konzept".
Ein Stream ist die Verbindung zwischen einer Datenquelle und einem Datenziel. Die Verbindung erfolgt immer in nur eine Richtung (von der Quelle zum Ziel). |
Um Eingaben in Java zu erhalten muß man einen Strom öffnen der mit einer Datenquelle verbunden ist und anschließend die Daten sequentiel lesen.
Für Ausgaben muß man einen Datenstrom zu einem Datenziel öffnen, dann die Daten sequentiell schreiben.
Dieses Konzept ist einem Wasserschlauch ähnlich: Sie benötigen jemand der das Wasser einspeißt und einen anderen Konsumenten der das Wasser wieder entnimmt.
Kategorien von Streams
In Java werden zwei Kategorien unterschieden:
- Character-Streams: Transportieren 16 Bit Daten. Sie arbeiten mit dem Javatyp char der Unicodezeichen darstellt
- Byte-Stream: Transportieren 8 Bit Daten. Sie arbeiten mit dem Javatyp byte.
- Ihre Basisfunktionalität wird durch die abstrakten Klassen InputStream und OutputStream bereitgestellt.
Wichtig: Alle Klassen die von diesen vier Basisklassen spezialisiert werden haben als Endung den Namen der abstrakten Klasse!
Bsp.: Bei der Klasse FileWriter handelt handelt es sich um einen Ausgabestrom auf Dateien.
Bsp.: Bei der Klasse FileWriter handelt handelt es sich um einen Ausgabestrom auf Dateien. Frage: Verwaltet er Bytes oder Zeichen? |
Interessant:Man kann Streams (Datenströme) verketten! Das bedeutet, dass ein Streamsobjekt in der Regel einen anderen Stream als Eingabe oder Ausgabe akzeptiert. Hiermit kann man Datenströme elegant umkonvertieren. |
java.nio und mehr...
Seit der Version JDK 1.4 gibt es in Java das Paket java.nio mit einer Reihe neuer Klassen. Diese Paket ist nicht Gegenstand der Vorlesung. In diesem Abschnitt werden nur einige wenige, ausgewählte Methoden der Klassen vorgestellt. Bitte benutzen Sie die Hyperlinks zur Java API Dokumention und einen vollständigen Überblick zu bekommen. |
- 1778 views
Die Klasse File
Die Klasse FileViele Operationen werden auf Dateien ausgeführt. Die Klasse File im Paket java.io erlaubt das manipulieren von Dateien und Verzeichnissen.
Im Beispiel der Klasse s2.io.DateiTest (github) kann man sehen wie man Verzeichnisse anlegt und ausliest und neue Dateien anlegt. Methoden zum Manipulieren und Auslesen von Dateiattributen sind in der Java Dokumentation zur Klasse File beschrieben.
package s2.io; /** * @author s@scalingbits.com */ import java.io.File; import java.io.IOException; public class DateiTest { /** * Hauptprogamm * @param args wird nicht verwendet... */ public static void main(String[] args) { String d ="testDir"; String f1 = "datei1.txt"; String f2 = "datei2.txt"; File dir = new File(d); File file1 = new File(d + "/" + f1); File file2 = new File(d + "/" + f2); if (dir.exists()) System.out.println("Verzeichnis " + d + " existiert bereits"); else dir.mkdir(); try { file1.createNewFile(); System.out.println("Datei wurde angelegt in : " + file1.getAbsolutePath() ); file2.createNewFile(); System.out.println("Datei wurde angelegt in : " + file2.getAbsolutePath() ); System.out.println("Dateien im Verzeichnis " + dir.getAbsolutePath()); String[] alleDateien = dir.list(); for ( String f : alleDateien) System.out.println("* " + f); } catch (IOException ex) { System.out.println("Probleme im IO Subsystem. Scotty beam me up!"); } } }
Übung: Erweitern Sie Anwendung so, dass die Dateien und das Verzeichnis wieder gelöscht werden! Frage: Welche Methoden brauche ich hierfür? Sie werden doch nicht auf diesen Hyperlink klicken: github s2.io.DateiTestLoesung.java |
- 1026 views
Einführung in Reader- und Writerklassen
Einführung in Reader- und WriterklassenDie Reader und Writer Klassen in Java erlauben es Zeichen-Stöme zu verwalten.
Die spezialisierten Klassen FileReader und FileWriter haben Konstruktoren in denen man als Quelle bzw. Ziel eine Datei angeben kann:
Ihre Oberklassen InputStreamReader und OutputStreamWriter haben Konstruktoren die die Konvertierung von Streams erlauben.
Hiermit kann man einen Byte-Stream (inputStream) in einen Zeichen-Stream (Reader) verwandeln. Dies ist nützlich wenn man eine Binärdatei (Byte-Stream) in eine Datei (Zeichen-Stream) schreiben möchte.
Im folgenden Beispiel wird gezeigt wie man einen Zeichen-Stream von der Konsole lesen kann und ihn dann in eine Datei schreibt. Die Java Klasse System bietet mit dem Attribute System.in Zugriff auf einen InputStream mit Konsoleneingaben und auf Konsolenausgaben (System.out).
/* * Ein Beispielprogramm welches von der Konsole liest und in eine Datei schreibt */ package s2.io; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.io.Writer; /** * * @author s@scalingbits.com */ public class SchreibInDatei { /** * Hauptprogamm * @param args wird nicht verwendet... */ public static void main(String[] args) { try { String f = "scalingbits.txt"; File datei = new File(f); Reader rein = new InputStreamReader(System.in); Writer raus = new FileWriter(datei); System.out.println("Der Text der jetzt eingegeben wird, wird in " + "der Datei " + f + " gespeichert"); System.out.println("Abschliessen mit Strg-Z oder Ctrl-Z"); System.out.println("Abschliessen Auf Unix/Linux mit Ctrl-D"); System.out.println("Abschliessen auf Mac mit IntelliJ mit Cmd-D"); umkopieren(rein,raus); } catch (IOException ex) { System.out.println("Probleme im IO Subsystem. Scotty beam me up"); } } /** * Umkopieren zwischen zwei Streams * @param r Eingabestream * @param w Ausgabestream * @throws IOException */ public static void umkopieren(Reader r, Writer w) throws IOException{ int c; while ((c= r.read()) != -1 ) { w.write(c); } r.close(); w.close(); } }
Übung: Erweitern Sie Anwendung so, dass sie die geschriebene Datei wieder ausliest und auf der Konsole druckt. Die Methoden umkopieren() kann man wiederverwenden... Frage: Welche Klassen brauche ich hierfür? Sie werden doch nicht auf diesen Hyperlink klicken: github s2.io.SchreibInDateiLoesung.java |
Gepufferte Reader- und Writer Klassen
Das Lesen bzw. Schreiben einzelner Bytes oder Zeichen vom Netzwerk oder von Dateien ist oft sehr ineffizient. Zum effizienteren Behandlung größerer Datenmengen stellt Java die Klassen BufferedReader und BufferedWriter zur Verfügung. Diese Klassen speichen die Daten zwischenzeitlich in einem Puffer und Lesen bzw. Schreiben die Daten in größeren Blöcken. Diese gepufferten Klassen müssen mit den elementaren Klassen verkettet werden. Deshalb haben sie die folgenden Konstruktoren:
- public BufferedReader(Reader in): zur Erzeugung eines gepufferten Stroms zum Lesen
- public BufferedWriter(Writer out): zur Erzeugung eines gepufferten Stroms zum Schreiben
Sie Verfügen über zusatzliche Methoden zum gepufferten Lesen und Schreiben
- BufferedReader.readLine(): Liest eine Textzeile bis zum nächsten Zeilenumbruch ('\n' oder '\r')
- BufferedWriter.newLine(): Schreibt einen Zeilenwechsel in den Puffer.
Nutzt man diese Klassen im Beispiel, gibt es eine Reihe von Veränderungen:
- mit BufferedReader.readLine() erhält man eine Zeichenkette als Ergebnis (oder einen Nullzeiger!)
- man sollte beim Schreiben den Zeilenimbruch selbst einfügen
Die Klasse SchreibInDateiGepuffered
/* * Eins Beispielprogramm welches von der Konsole liest und in eine Datei schreibt */ package s2.io; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; /** * * @author s@scalingbits.com */ public class SchreibInDateiGepuffered { /** * Hauptprogamm * @param args wird nicht verwendet... */ public static void main(String[] args) { try { String f = "scalingbits.txt"; File datei = new File(f); BufferedReader rein = new BufferedReader( new InputStreamReader(System.in)); BufferedWriter raus = new BufferedWriter( new FileWriter(datei)); System.out.println("Der Text der jetzt eingegeben wird, wird in " + "der Datei " + f + " gespeichert"); System.out.println("Abschliessen mit Strg-Z oder Ctrl-Z"); System.out.println("Abschliessen Auf Unix/Linux mit Ctrl-D"); System.out.println("Abschliessen auf Mac mit IntelliJ mit Cmd-D"); umkopieren(rein,raus); rein = new BufferedReader( new FileReader(f)); raus = new BufferedWriter( new OutputStreamWriter(System.out)); System.out.println("Ausgabe des in Datei " + f + " gespeichertem Texts"); umkopieren(rein,raus); } catch (IOException ex) { System.out.println("Probleme im IO Subsystem. Scotty beam me up"); } } /** * Umkopieren zwischen zwei Streams * @param r Eingabestream * @param w Ausgabestream * @throws IOException */ public static void umkopieren(BufferedReader r, BufferedWriter w) throws IOException{ String z; while ((z= r.readLine()) != null) { w.write(z); w.newLine(); } r.close(); w.close(); } }
Die Datei SchreibInDateiGepuffered in github.
- 4910 views
Behandlung von Byte-Strömen
Behandlung von Byte-StrömenVon den Byte-Streams werden hier nur wenige ausgewählte vorgestellt
Die abstrakte Basisklasse InputStream hat für die Eingabe von Byte-Streams unter anderem die folgenden Methoden:
- public abstract int read(): sie liefert das nächste Byte aus dem Strom (im Format int!)
- public int read(byte[] b): füllt das Feld der Bytes und gibt als Rückgabe die Anzahl der gelesenen Bytes
- public int read(byte[] b, int off, int n): füllt das Feld der Bytes ab der Position off mit n Bytes und liefert die Anzahl der gelesenen Bytes zurück
- public void close(): Schliest den Strom
Die abstrakte Basisklasse OutputStream besitzt ähnliche Methoden
- public abstract void write(int b): schreibt das Byte b in den Strom
- public void write(byte[] b): schreibt alles von b referenzierten Bytes in den Strom
- public void write(byte[] b, int off, int n): schreibt ab dem Index off die nächsten n Bytes in den Strom
- public void close(): schliest den Strom
- public void flush(): leert den Puffer und schreibt alle Daten in den Strom
DataInput- und DataOutputStream
Die Klassen DataInputStream und DataOutputStream erlauben das Lesen und Schreiben von Java-Basistypen. Ihre Konstruktoren arbeiten mit den Basisklassen:
- public DataInputStream(InputStream in): Erlaubt das Lesen von einem InputStream
- public DataOutputStream(OutputStream out): Schreibt in einen OutputStream
Diese Klassen stellen dann typspezifische Methoden mit der folgenden Notation zur Verfügung. Zum Beispiel DataOutputStream:
- public xxx readxx(): Lies den Typ xxx und gib ihn aus
Serialisierung und Deserialisierung von Objekten
Die Klassen ObjectInputStream und ObjectOutputStream erlauben es Objekte aus dem Hauptspeicher in einen Stream zu schreiben.
Sie wandeln den systemabhängigen Zustand eines Objects in eine neutrale Binärdarstellung in einem Datenstrom um. Diesen Vorgang nennt man Serialisierung in Java. Den umgekehrten Vorgang nennt man Deserialiserung.
Wichtig: Es könne nur Objekte serialiert werden die die Schnittstelle Serializable implementieren. Diese Schnittstelle verlangt es nicht Methoden zu implementieren.
Alle referenzierten, serialisierbaren Objekte werden auch serialisiert.
Möchte man Instanzvariablen nicht serialisieren, muss man sie mit Schlüsselwort transient kennzeichnen.
Die beiden Klassen ObjectInputStream und ObjectOutputStream haben Konstruktoren, die es erlauben aus bzw. in andere Ströme zuschreiben:
- public ObjectInputStream(InputStream in): lesen aus einem InputStream
- public ObjectOutputStream(OutputStream out): schreiben in einen OutputStream.
In geöffnete Ströme vom Typ ObjectInputStream kann man mit der folgenden Methoden lesen:
- public final Object readObject(): liest ein Objekt aus dem Datenstrom und gibt einen Zeiger zurück.
Ähnlich kann in ObjectOutputStream mit der folgenden Methode ein Objekt schreiben
- public final void writeObject(Object obj): schreibt ein Object und alle Unterobjekte in den Strom
- 1303 views
Weitere Klassen
Weitere KlassenDas java.io Paket hat noch viele weiter Klassen. Hier eine kurze Liste der interessantesten Klassen
- RandomAccessFile: wahlfreier Zugriff auf Dateien
- InflaterInputStream, DeflaterOutputStream: checken der Integrität von Dateien mit Hilfe von Prüfsummen
- ZipInputStream, ZipOutputStream: komprimierende, dekomprimierende Ströme. Für gzip gibt es analoge Klassen
- 797 views
Übungen ( Streams)
Übungen ( Streams)Vokalverschiebung
Ersetzen Sie alle Vokale in einer Textdatei durch bestimmte andere und erzeugen Sie eine neue Datei mit den verschobenen Vokalen.
Eine Musterlösung ist in github zu finden.
- 1342 views
Lernziele
Lernziele
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zur Streams
- 762 views
Rechtschreibfehler
Definition vom Stream, zweiter Satz:
ein "in" zu viel :)
Korrekt.
Danke, wurde korrigiert.