Writer - TU Darmstadt

Werbung
Grundzüge der Informatik
Teil 5.1 Ein- und Ausgabe in Java
Prof. Dr. Max Mühlhäuser
FG Telekooperation
TU-Darmstadt
Agenda
•
•
•
•
•
•
Warum Ein-/Ausgabe in Java?
Ein-/Ausgabeströme und -oberklassen
Zeichenströme und Byteströme
Dateiströme, Filter, Puffer und PushBack
Tokenizer
Freier (random) Zugriff
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
2
Eingabe /Ausgabe (Input / Output, I/O)
• Bislang wurden Eingabedaten fest im Quelltext codiert
• Zusätzlich wurden die Werte als Argument bei
Programmstart übergeben
– java fakultaet 7
• Folgen dieser Einschränkungen:
– Geringe Flexibilität der Programme und Algorithmen
– Datenänderung erfolgt durch Programmänderung und
Neuübersetzung
– Ungeeignet für größere oder komplexere Daten
– Keine Interaktionsmöglichkeit
– Ergebnisse sind nicht speicherbar
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
3
Eingabe /Ausgabe (Input / Output, I/O)
• Ziele von I/O:
– Speicherung von Ergebnissen
– Verarbeitung von gespeicherten Daten
– Einbeziehen weiterer gespeicherter Medien, etwa Bilder
• Woher stammen die Daten eines Programmes?
–
–
–
–
–
–
–
Codiert im Programmtext
Parameter bei Programmstart
Tastatureingabe („Standard I/O“)
Datei(en) auf dem lokalen Rechner („File I/O“)
Datei(en) im Netzwerk („Network I/O“)
Ergebnisse / Eingaben eines anderen Programms („Piped I/O“)
Hauptspeicher („Memory I/O“)
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
4
I/O-Ströme
• Um Daten ein- / auszugeben, wird in Java das Konzept der „Streams“
(Ströme, Datenströme) eingeführt
• Datenströme sind eine Abstraktion von Eingabe- bzw.
Ausgabeverbindungen des Programms zu externen “Datenbehältern“
– Beispiele, s.o.: Tastatur, Datei, anderes Programm, Netz, Hauptsp., …
– Grundlage ist Unix (in Java wesentlich erweitert), dort gilt:
ein Datenbehälter sendet bzw. empfängt unstrukturierte Ströme von Bytes
• Ströme stellen Java-Programmen einheitliche Schnittstellen zum Lesen
bzw. Schreiben von Daten zur Verfügung
• Ströme verstecken Details über die Implementierung u. Funktionsweise
der einzelnen I/O Geräte vor dem Java Programmierer
– Das Java Programm “spricht“ mit Java I/O Objekten
• Man unterscheidet Eingabe- und Ausgabeströme
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
5
I/O-Ströme
• Um Daten von einer externen Datenquelle zu lesen, öffnet ein JavaProgramm einen Eingabestrom und liest die Daten (nacheinander) ein:
• Um Daten in eine externe Senke zu schreiben, öffnet ein JavaProgramm einen Ausgabestrom und schreibt die Daten seriell
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
6
Verschachtelung der Ströme
• Das externe Medium kann ein anderer Datenstrom sein
• Ströme können ineinander verschachtelt sein, indem ein Strom einen
anderen unterliegenden Strom einkapselt und ihn als Datenquelle bzw.
Senke benutzt
BufferedReader
StreamTokenizer
File Reader
Datenquelle,
z.B. Datei
• Sinn: Daten können vor dem Schreiben auf den unterliegenden Strom
bzw. nach dem Lesen bearbeitet werden
• àAbstraktionsebenen, bei denen unterliegende „primitive“ Ströme
von einkapselnden („höheren“, komfortableren) Strömen benutzt
werden („Prozessströme“)
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
7
Kategorisierung der vordefinierten Ströme
• java.io enthält eine Sammlung von Strom-Klassen
• Zwei Kategorisierungen von Datenströmen:
– Zeichenströme / Byteströme
unglücklicher Begriff!
– Datensenkeströme / Prozessströme
besser: Datenbehälter
Klassenhierarchie
Klassenhierarchie
der
derZeichenströme
Zeichenströme
Klassenhierarchie
Klassenhierarchie
der
derByteströme
Byteströme
(engl.: Wrapper)
Klassen
Klassenfür
für
Datensenkeströme
Datensenkeströme
Klassen
Klassenfür
für
Prozessströme
Prozessströme
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
8
Kategorisierung der vordefinierten Ströme
• Zeichenströme lesen / schreiben char (16-bit Unicode)
• Byteströme lesen / schreiben byte (8 bit)
• Datensenkeströme
– Daten werden direkt von konkreter Datenquelle
gelesen bzw. auf konkrete Datensenke geschrieben
• Prozessströme
– Daten werden von anderen Strömen gelesen bzw.
auf andere Ströme geschrieben
– Daten werden nach dem Lesen, bzw. vor dem
Schreiben bearbeitet
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
9
I/O-Ströme
• Die Algorithmen zum Lesen bzw. Schreiben von Daten
haben unabhängig von Datentyp und Quelle bzw. Senke
immer die gleiche Form:
• Lesen
• öffne einen Strom
• solange es noch Daten gibt lese Daten
• schließe den Strom
• Schreiben
• öffne einen Strom
• solange es noch Daten gibt schreibe Daten
• schließe den Strom
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
10
Eingabe
I/O-Oberklassen
Reader und InputStream definieren ähnliche
Schnittstellen, aber für verschiedene Datentypen:
InputStream
public int read()
public int read(byte[] bbuf)
public int read(byte[] bbuf, int offset, int len)
Reader
public int read()
public int read(char[] cbuf)
public int read(char[] cbuf, int offset, int len)
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
11
Ausgabe
I/O-Oberklassen
• Writer und OutputStream definieren ähnliche
Schnittstellen, parallel zu Reader und InputStream:
OutputStream
public int write(int b)
public int write(byte[] bbuf)
public int write(byte[] bbuf, int offset, int len)
Writer
public int write(int c)
public int write(char[] cbuf)
public int write(char[] cbuf, int offset, int len)
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
12
I/O-Oberklassen
• Ströme werden beim Erzeugen automatisch geöffnet
• Bei Beenden des Lesens bzw. Schreibens sollte der Strom
durch close() geschlossen werden
• Der Java Garbage Collector schließt notfalls einen Strom,
auf den nicht mehr verwiesen wird
– Dennoch sollte das close() immer explizit erfolgen!
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
13
Zeichenströme
• Zeichenströme ermöglichen das Lesen bzw. Schreiben von
16-Bit-Zeichen
• java.io.Reader/java.io.Writer stellen eine
Schnittstelle und eine partielle Implementierung für Eingabebzw. Ausgabe-Ströme zur Verfügung
• Unterklassen von Reader/Writer konkretisieren bzw.
spezialisieren die Implementierung in Reader/Writer
• Zeichenströme können alle Zeichen in der UnicodeZeichenmenge lesen und schreiben.
– Dagegen sind Byteströme beschränkt auf ISO-Latin-1 Bytes
• Die meisten Programme benutzen Reader/Writer, um
Informationen zu lesen bzw. zu schreiben
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
14
Hierarchie der Zeichenströme
Reader
StringReader
InputStreamReader
InputStreamReader
FileReader
FilterReader
FilterReader
PushbackReader
PushbackReader
BufferedReader
BufferedReader
LineNumberReader
LineNumberReader
Die Klassen in schattierten Kästchen sind Prozessströme
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
15
Hierarchie der Zeichenströme
Writer
StringWriter
OutputStreamWriter
OutputStreamWriter
FileWriter
FilterWriter
FilterWriter
BufferedWriter
BufferedWriter
PrintWriter
PrintWriter
Die Klassen in schattierten Kästchen sind Prozessströme
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
16
Byteströme
• Byteströme stellen Funktionalität zum Lesen bzw. Schreiben von
Bytes zur Verfügung
• Normalerweise werden Byteströme zum Lesen bzw. Schreiben von
Binärdaten, z.B. Bildern, benutzt
• java.io.InputStream/java.io.OutputStream stellen die
gemeinsame Schnittstelle und partielle Implementierung für alle
Ströme zum Lesen bzw. Schreiben von Bytes zur Verfügung
• Alle Byteströme sind Unterklassen von
InputStream/OutputStream
• Im folgenden werden durchgehend nur Zeichenströme betrachtet
– Zumeist gibt es entsprechende Klassen für Byteströme
– Bitte sehen Sie auch in die Java Dokumentation für Details
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
17
Nutzung der Klasse java.io.Reader
• Objekte der Unterklassen von Reader lesen Zeichen aus
einer bestimmten Quelle (Datei, Netzwerk,...)
• Die wesentlichen Methoden der Klasse sind …:
– public abstract int read() throws IOException
• Liefert das nächste Unicode-Zeichen im Strom
• -1 falls keine weiteren Zeichen vorhanden sind
(End of File, „EOF“)
• IOException bei allen anderen Problemen
– Z.B. Strom bereits geschlossen, Netzwerkverbindung verloren, ...
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
18
Nutzung der Klasse java.io.Reader
• public abstract int read()
Programm
Datenquelle
Reader
Datenquelle
Programm
Reader
Datenquelle
-1
Programm
Reader
15. 7. 2003 © MM ...
IOException
GDI - Ein- und Ausgabe in Java
Legende
Gelesene Zeichen
Nächstes Zeichen
EOF
geöffneter
Strom
geschlossener
Strom
19
Nutzung der Klasse java.io.Reader
• public int read(char[] c) throws IOException,
NullPointerException
– liest eine Anzahl von Zeichen und speichert sie in dem charArray c. Ruft read() wiederholt auf
– gibt Anzahl der gelesenen Zeichen zurück bzw. -1, wenn
aufgrund Dateiende (EOF) kein Zeichen gelesen werden konnte
– NullPointerException, wenn c == null
– IOException, wenn das erste Zeichen nicht gelesen werden
konnte aus irgendeinem anderen Grund als EOF
– I/O Ausnahmen während des Lesens von anderen Zeichen
werden abgefangen (behandelt anstelle sie weiterzugeben) und
die Anzahl der Zeichen soweit gelesen zurückgegeben
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
20
Nutzung der Klasse java.io.Reader
Programm
Datenquelle
public int
read(char[] c)
Legende
Reader
c
Datenquelle
Programm
Reader
c
Gelesene Zeichen
Nächstes Zeichen
-1
Programm
Datenquelle
EOF
Reader
c
geöffneter
Strom
geschlossener
Strom
15. 7. 2003 © MM ...
3
IOException
Programm
Datenquelle
Reader
GDI - Ein- und Ausgabe in Java
c
2
21
Nutzung der Klasse java.io.InputStream
• public long skip(long n) throws IOException
– versucht n Zeichen von dem Eingabestrom zu überspringen
– Zurückgegeben wird die Anzahl übersprungener Zeichen
– die Anzahl der tatsächlich übersprungenen Zeichen kann kleiner
als n sein:
• EOF wurde erreicht bevor n Zeichen übersprungen wurden
• IOException, wenn das erste Zeichen nicht gelesen werden
konnte aus irgendeinem anderen Grund als EOF
• public void reset() throws IOException
– Versucht den Strom zurückzusetzen (wird nicht von allen
Strömen unterstützt)
• public int close() throws IOException
– Schließt den Strom. Aus einem geschlossenen Strom kann nicht
mehr gelesen werden.
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
22
Nutzung der Klasse java.io.Writer
• Writer implementiert einen Zeichenstrom, der Zeichen
erwartet und sie zu einer Senke schickt
• public abstract void write(int c) throws
IOException
– schreibt ein Zeichen (8 low-order Bits von c) auf die Senke
• public void write(char[] c) throws IOException,
NullPointerException
– schreibt alle Zeichen aus c sequentiell auf den Strom. Ruft
write(int) wiederholt auf.
• void write(String s, int off, int len) throws
IOException
– Schreibt die gewählten Zeichen aus dem String durch mehrfaches
write(int) auf den Strom
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
23
Nutzung der Klasse java.io.Writer
• public abstract void flush() throws
IOException
– Alle vor dem Aufruf von flush() geschriebenen
Zeichen, die eventuell innerhalb des Stroms gepuffert
sind, werden sofort auf die Senke des Stroms
geschrieben
– Der Vorgang wird dabei bei geschachtelten
Ausgabeströmen auf alle Ströme fortgesetzt.
• Für weitere Methoden siehe die Beschreibung in
der Java API Dokumentation
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
24
Datensenkeströme: Überblick
Senkentyp
Zeichenströme
Hauptspeicher
StringReader
StringWriter
Datei
FileReader
FileWriter
StringReader und StringWriter ermöglichen das
Lesen von bzw. Schreiben auf Strings im Hauptspeicher
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
25
Dateiströme
• Dateiströme stellen Eingabe-/Ausgabe-Ströme zur
Verfügung, deren Quellen/Senken Dateien im Dateisystem
sind:
– FileReader für das Lesen sowie
– FileWriter für das Schreiben
• Ein Dateistrom kann erzeugt werden, indem man die
Quelle- bzw. Senke-Datei durch eines der folgenden
Objekte als Parameter des Strom-Konstruktors eingibt:
– Dateiname (String)
– Datei-Objekt (java.io.File)
– Dateibeschreibung (java.io.FileDescriptor)
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
26
Die Klasse java.io.FileReader
• FileReader stellt einen Zeichen-Eingabe-Datenstrom
dar, dessen Quelle eine Datei im Dateisystem ist
• public FileInputStream(String path) throws
FileNotFoundException
– Erzeugt einen neuen Dateilesestrom durch Öffnen der
benannten Datei
– FileNotFoundException, falls die Datei nicht existiert
• Die Beschreibung passt auch auf die Varianten mit
Parameter java.io.File oder java.io.FileDescriptor
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
27
Die Klasse java.io.FileReader
Beispiel: Ausgeben eines Dateiinhalts auf den Bildschirm
Achtung: Ausgabe erfolgt als Integer-Werte!
import java.io.FileReader;
import java.io.IOException;
public class PrintFile {
public static void main(String[] args)
throws IOException {
// setzt mindestens einen Parameter voraus!
FileReader in = new FileReader(args[0]);
int b;
while ((b = in.read()) != -1)
System.out.print(b);
in.close();
}
}
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
28
Die Klasse java.io.FileWriter
• FileWriter stellt einen Ausgabe-Strom dar, der in eine
Datei schreibt
• Beispiel: Die Zeichen von 'a' bis 'z' in eine Datei schreiben:
public void writeAToZ(String filename) throws IOException {
FileOutputStream out = new FileOutputStream(filename);
for (char c = 'a'; c <= 'z'; c++)
out.write(c);
out.close();
}
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
29
Prozess-Ströme
Prozess-Ströme sind Datenströme, die zusätzliche
Operationen auf den Daten, die von einem unterliegenden
Datenstrom gelesen, bzw. geschrieben werden,
vornehmen:
• Zwischenspeichern (Puffern) von Daten während des
Schreibens/Lesens auf/von dem Ursprungs-Datenstrom.
• Zählen der gelesenen/geschriebenen Zeilen
• Konvertierung zwischen Byte und Zeichen
• …
Prozess-Datenstrom Originaler Datenstrom
Datensenke
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
Datenquelle
30
Prozess-Ströme
Prozess
Zeichenströme
Filterung
FilterReader,
FilterWriter
Pufferung
BufferedReader,
BufferedWriter
Byte / Char konvertieren InputStreamReader,
OutputStreamWriter
Zeile zählen
LineNumberReader
Lesen von Datentypen
[ DataInputStream ]
Zurücklegen
PushbackReader
Drucken
PrintWriter
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
31
java.io.FilterReader/-Writer
• Enthält einen anderen Strom, der als Datenquelle bzw.
Datensenke benutzt wird
• Daten werden möglicherweise transformiert, oder
zusätzliche Funktionalität wird zur Verfügung gestellt
• FilterReader und FilterWriter sind Oberklassen aller
Filter-Ströme
– Unterklassen von Reader / Writer
– Überschreiben alle Methoden der jeweiligen Oberklassen, so
dass die Aufrufe lediglich an den unterliegenden Strom
weitergegeben werden
– Konkrete Filter-Ströme (Unterklassen) spezialisieren diese
Funktionalität
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
32
Die Klassen java.io.PrintStream/-Writer
• Unterklasse von FilterOutputStream bzw. Writer
• Erweitern einen Ausgabestrom um die Ausgabe einer
String-Darstellung verschiedener Daten
• PrintStream, PrintWriter lösen nie Ausnahmen aus
• Stattdessen wird eine interne Markierung gesetzt,
die mit der Methode checkError() abgefragt werden
kann
• Die Funktionalität der beiden Klassen ist identisch
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
33
Die Klassen java.io.PrintStream/-Writer
Wichtigste Methoden:
• public void print(X x)
• Ausgabe von x auf dem PrintStream
• public void println(X x)
• Ausgabe von x auf dem PrintStream, gefolgt
von einem Zeilenwechsel
• Dabei steht X für...
• jeden primitiven Typ (int, char, double, ...)
• String
• sowie Object
• Standarddarstellung ist nicht „lesbar“
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
34
Standard-I/O
Im Paket java.lang gibt es die Klasse System mit den
folgenden Konstanten (Klassenvariablen):
System.in
System.out
System.err
Standardeingabe (Tastatur)
Standardausgabe (Bildschirm)
Fehlermeldungen (Bildschirm)
•
System.in ist ein Objekt vom Typ InputStream
– stellt read() zum byteweisen Lesen von Bytes
von der Tastatur zur Verfügung
•
System.out, System.err sind vom Typ PrintStream
– bieten print() und println() zur Datenausgabe an
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
35
Standard I/O
Beispiel: Lesen eines Zeichens von der Tastatur
import java.io.IOException;
public class StandardIOExample {
public static void main(String[] args) {
System.out.print(“>“);
try {
char c = (char) System.in.read();
System.out.println(c);
}
catch(IOException e) {
System.err.println(e.getMessage());
}
Bemerkung: Exceptions werden hier durch
}
Ausgabe der Fehlermeldung „behandelt“
}
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
36
Standard I/O
System.in
System.out
System.err
Standardeingabe (Tastatur)
Standardausgabe (Bildschirm)
Fehlermeldungen (Bildschirm)
• Beim Starten von Java sind alle drei Ströme geöffnet
• Die Ausgabe kann vom Programmierer umgeleitet
werden durch Nutzung folgender Methoden von
java.lang.System:
public static void setIn(InputStream in)
public static void setOut(PrintStream out)
public static void setErr(PrintStream err)
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
37
Pufferströme
• Ein Datenstrom mit Pufferung kapselt einen anderen UrsprungsDatenstrom und einen internen Puffer
• Beim Schreiben werden die Daten zuerst in dem internen Puffer
gespeichert
• Nur wenn der Puffer voll ist, wird auf den unterliegenden Strom
geschrieben
– Sowie bei explizitem Aufruf der Methode flush()
• Beim ersten Lesen wird der Puffer vollständig gefüllt
• Weitere Lese-Operationen auf dem Strom liefern Bytes vom
Puffer zurück, ohne vom unterliegenden Strom tatsächlich zu
lesen
– Bei leerem Puffer wird erneut vom unterliegenden Strom gelesen
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
38
Pufferströme
• BufferedReader und BufferedWriter bieten die
Standard-Schnittstelle und Implementierung für
Zeichenströme mit Pufferung an
– Sie sind Unterklassen von Reader bzw. Writer
• BufferedInputStream und BufferedOutputStream
bieten die Standard-Schnittstelle und
Implementierung für Byteströme mit Pufferung an
– Sie sind Unterklassen von FilterInputStream bzw.
FilterOutputStream
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
39
Pufferströme
• BufferedWriter erweitert Zeichen-Ausgabeströme
um die Fähigkeit, Daten zu puffern
• Zeichen können geschrieben werden, ohne für
jedes Zeichen einen Zugriff auf dem
unterliegenden Strom starten zu müssen
• Daten werden auf den unterliegenden Strom
geschrieben, wenn:
•
•
•
die Kapazität des internen Puffers erschöpft ist,
der Strom geschlossen wird durch close()
flush() explizit aufgerufen wird
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
40
Puffer-Ströme: java.io.BufferedWriter
Datensenke
BufferedWriter
Programm
b
b
c
write
bbuf
bbuf
cbuf
write
write bbuf
bbuf
cbuf
write bbuf
bbuf
cbuf
flush
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
41
Pufferströme
• BufferedReader erweitert Zeichen-Eingabeströme
mit Pufferung der Daten
• Zeichen können von einem BufferedReader gelesen
werden, ohne für jedes zu lesendes Zeichen einen
Zugriff auf dem unterliegenden Stream starten zu
müssen
• Bei der ersten Lese-Operation werden so viele Daten
von dem unterliegenden Strom gelesen, wie der
Puffer fasst
• Folgende Lese-Operationen lesen die Daten vom
Puffer und nicht vom System
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
42
Pufferströme: Beispiel
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
public class BufferDemo {
public BufferDemo(String filename) {
for (int i=1; i<65536; i = i * 2)
testBuffer(filename, i);
}
// testBuffer auf naechster Folie!
public static void main(String[] args) {
BufferDemo bufferDemo = new BufferDemo(args[0]);
}
}
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
43
Pufferströme: Beispiel
public void testBuffer(String fname, int bufferSize)
{
try {
FileReader fis = new FileReader(fname);
BufferedReader bis = new BufferedReader(fis,
bufferSize);
int i; // read byte
long timeNow = System.currentTimeMillis();
while ((i = bis.read()) != -1)
; // do nothing here...
bis.close();
System.out.println("Size: " +bufferSize +",time:"
+(System.currentTimeMillis()-timeNow));
} catch(IOException e) {
System.err.println(e.getMessage());
}
}
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
44
Pufferströme: Beispiel
• Ergebnis für eine 650kB-Datei (x=Puffergröße, y=Zeit [ms])
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
45
BufferedReader
Reader
BufferedReader
Reader()
read()
BufferedReader(Reader in)
BufferedReader(Reader in, int size)
readLine()
LineNumberReader
LineNumberReader(Reader in)
LineNumberReader(Reader in, int size)
getLineNumber()
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
46
Pushback-Ströme
• Ein Pushback-Strom kapselt einen anderen
Ursprungsdatenstrom
• Ein vom unterliegenden Datenstrom gelesenes Datum
(Byte bei PushbackInputStream bzw. char bei
PushbackReader) kann „zurückgelegt“ werden („push
back“ oder „unread“)
• Die nächste read() Operation an den Pushback-Strom
wird das zurückgelegte Datum erneut lesen
• Man kann also ungestraft ein Byte bzw. Zeichen
“vorausschauen“
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
47
Pushback Strom: Illustration
Datenquelle
Programm
PushbackReader
read
pushBack
unread
read
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
48
Datenströme
• Daten-Ströme ermöglichen das bequeme Lesen und
Schreiben von primitiven Datentypen (anstatt
einzelner Zeichen)
• Sie existieren nur als Byte-, nicht als
Zeichenströme
• Ein DataInputStream liest Bytes von einer EingabeQuelle und interpretiert spezifische
Zeichensequenzen als Darstellungen von Daten
unterschiedlicher Typen
• Eine Anwendung benutzt DataOutputStream zum
Schreiben von Daten, die später von einem
DataInputStream gelesen werden können
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
49
Datenströme
FilterInputStream
FilterOutputStream
DataInputStream
DataOutputStream
readBoolean(): boolean
readByte(): byte
readShort() : short
readChar() : char
readInt() : int
readFloat() : float
writeBoolean(boolean) : void
writeByte(byte) : void
writeShort(short) : void
writeChar(char) : void
writeInt(int) : void
writeFloat(float) : void
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
50
Datenströme: Beispiel
import java.io.FileOutputStream;
import java.io.DataOutputStream;
import java.io.IOException
public class DataStreamExample {
// …
public void writeData(String filename) throws
IOException {
FileOutputStream fileOut = new
FileOutputStream(filename);
DataOutputStream out = new
DataOutputStream(fileOut);
out.writeInt(9);
out.writeDouble(Math.PI);
out.writeBoolean(true);
out.close();
} // …
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
51
Datenströme: Beispiel
public void readData(String filename)
throws IOException {
FileInputStream fileIn =
new FileInputStream(filename);
DataInputStream in = new DataInputStream(fileIn);
int i = in.readInt();
double d = in.readDouble();
boolean b = in.readBoolean();
in.close();
System.out.println("Read " + i + ", " + d
+ ", and " + b + ".");
}
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
52
Selbst definierte Prozessströme
• Oft möchten Programmierer eigene Ströme definieren,
die Daten filtern und/oder verarbeiten, die von einem
Datensenke-Strom gelesen/geschrieben werden
– Verarbeitung unabhängig vom Datenformat,
z.B. zum Zählen von bestimmten Einheiten im Strom
– Verarbeitung abhängig vom Datenformat,
z.B. Lesen von Daten organisiert in Spalten und Zeilen
• Eigene Filterung oder Verarbeitung kann implementiert
werden, indem man von FilterReader bzw. FilterWriter
erbt
• Es folgt ein Beispiel eines selbstdefinierten Filterstroms
auf Datenströmen, der alle Zeilen im unterliegenden
Strom herausfiltert, die einen bestimmten Substring
enthalten
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
53
Selbst definierte Prozessströme
import java.io.DataInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
public class GrepInputStream extends FilterInputStream {
String substring;
DataInputStream in;
public GrepInputStream(DataInputStream in, String substring) {
super(in);
this.in = in;
this.substring = substring;
}
public final String readLine() throws IOException {
// Gib die naechste Zeile aus, die den substring enthaelt
String line;
while ((line = in.readLine()) != null &&
line.indexOf(substring) == -1) {}
return line;
}
}
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
54
Selbst definierte Prozessströme
import java.io.*; // IOException, DataInputStream, FileInputStream
public class Grep {
public static void main(String[] args) {
if ((args.length == 0) || (args.length > 2)) {
System.out.println("Usage: java Grep <substring> [<filename>]");
System.exit(0);
}
try {
DataInputStream d;
if (args.length == 2)
d = new DataInputStream(new FileInputStream(args[1]));
else
Das suchen
d = new DataInputStream(System.in);
GrepInputStream g = new GrepInputStream(d, args[0]);
String line;
while ((line = g.readLine()) != null)
Hierin suchen
System.out.println(line);
g.close();
} catch (IOException e) { System.err.println(e); }
}
}
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
55
Analyse von Strömen mit StreamTokenizer
• java.io.StreamTokenizer unterstützt die Zerlegung von
Strömen in Symbole („Tokens“)
• Ein Token gibt nur den Typ des gelesenen Elements an
• Lesen des nächsten Tokens durch nextToken()
• Der Typ wird über das Attribut ttype abgefragt liefert
(als int)
• Im wesentlichen gibt es fünf Arten Tokens:
–
–
–
–
–
StreamTokenizer.TT_NUMBER – Zahl erkannt
StreamTokenizer.TT_WORD – Wort erkannt
StreamTokenizer.TT_EOL – Zeilenende
StreamTokenizer.TT_EOF – Dateiende
„Sonstiges“ – ttype codiert das Zeichen, etwa '$', ';'
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
56
Analyse von Strömen mit StreamTokenizer
• Abfragen eines gelesenen Werts:
1. Checken des gelesenen Tokens durch Abfrage von ttype
(oder direkt bei nextToken())
2. Falls ttype == StreamTokenizer.TT_WORD:
– Zugriff auf String durch Attribut sval
3. Falls ttype == StreamTokenizer.TT_NUMBER:
– Zugriff auf Wert durch Attribut nval – Vorsicht, immer
double !
4. Ansonsten ist der Wert von ttype das gesuchte Element
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
57
Analyse von Strömen mit StreamTokenizer
StreamTokenizer sind anpassbar:
• public void wordChars(int low, int hi)
– Definieren, was als Wortbestandteil gelten soll
– Alle Zeichen im Intervall [low, hi] zählen als Wortbestandteile
• public void whitespaceChars(int low, int hi)
– Definieren von Leerzeichen
– Alle Zeichen im Intervall [low, hi] zählen als „Leerzeichen“
• public void eolIsSignificant(boolean flag)
– Definieren, ob Zeilenende relevant ist
• public void quoteChar(int quoteChar)
– Definieren von Anführungszeichen
• Zahlreiche andere Features – bitte in die Doku sehen!
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
58
Analyse von Strömen mit StreamTokenizer
Anwendungsbeispiel:
• Es soll eine Teeliste für einen Hobbyteetrinker
geparst werden
• Die Teeliste hat folgende Struktur:
– Jeder Tee steht in einer eigenen Zeile
• Das Zeilenende ist also relevant!
– Die Teenamen sind durch Anführungszeichen „gequotet“
– Hinter dem Teenamen stehen der Reihe nach
• Dauer des Ziehenlassens
• Anzahl Teelöffel pro Liter Tee
• Diese Einträge sind jeweils durch Leerzeichen getrennt
Mittels StreamTokenizer ist das Parsen einfach!
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
59
Analyse von Strömen mit StreamTokenizer
• Beispieldatei:
"Ali Babas 40 Düfte" 180 5
"Asatsuyu" 90 7
"Generic Black Tea" 180 5
"Caramel" 120 6
"Ceylon Pekoe" 120 6
"China Jasmin" 120 6
"Chinesischer Liebestraum" 150 5
"Cool Down" 1273 0
"Einer für alle" 540 6
"Erdbeer-Sahne" 120 6
"Erfrischungskräutertee" 480 6
"Früchtepfund" 420 6
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
60
Analyse von Strömen mit
StreamTokenizer
public void parseTeas(InputStream is) {
// InputStream sei schon geoeffnet...
StreamTokenizer stok = new StreamTokenizer(is);
stok.eolIsSignificant(true); // EOL melden
stok.quoteChar('\"'); // " als Worttrenner
int token = 0;
String teeName = null;
int nrSpoons, nrSecs, coolDown;
int eofCode = StreamTokenizer.TT_EOF;
// ... weiter auf naechster Folie
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
61
Analyse von Strömen mit
StreamTokenizer
while ((token = stok.nextToken()) != eofCode)
{
teaName = stok.sval; // 1. Token
token = stok.nextToken();
nrSpoons = (int)stok.nval; // 2. Token
token = stok.nextToken();
nrSecs = (int)stok.nval; // 3. Token
token = stok.nextToken(); // must be EOL
System.out.println(teaName +":" +nrSpoons
+" Loeffel, Ziehzeit: " +nrSecs);
}
}
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
62
Wahlfreier Zugriff
• Bisher wurden nur sequentielle Ströme behandelt,
deren Inhalt nur in einer sequentiellen Weise gelesen
bzw. geschrieben werden kann
• Sequentielle Dateien passen gut zu sequentiellen
Speichermedien, z.B. Magnetbänder
• Random Access Files erlauben nicht-sequentielle
(wahlfreie) Zugriffe auf den Inhalt einer Datei
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
63
Warum wahlfreier Zugriff?
• Gegeben ein Dateiarchiv in .zip Format:
– Zip-Archive bestehen aus Dateien und sind meist
komprimiert, um Speicher zu sparen
– Außer Dateien haben Zip-Archive ganz zum Schluss
zusätzlich einen so genannten Dir-Eintrag (von Directory,
engl. Verzeichnis)
– Hier wird gespeichert wird, wo die einzelnen Dateien
innerhalb des Archivs anfangen
• Aufgabe: eine bestimmte Datei aus dem Archiv extrahieren
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
64
Darum wahlfreier Zugriff!
• Verfahren für einen sequentiellen Strom:
– Suche durch das ganze Archiv bis die gewünschte Datei
lokalisiert ist
– Extrahiere die Datei
• Im Durchschnitt wird die Hälfte der Einträge im Archiv
gelesen, bevor die gewünschte Datei gefunden ist
• Strom mit wahlfreiem Zugriff:
– Springe zum Dir-Eintrag und lese den Eintrag der gesuchten
Datei
– Springe rückwärts zu der Position der Datei
– Extrahiere die Datei
• Man muss genau zwei Einträge lesen
• Das Verfahren ist also wesentlich effizienter!
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
65
Wahlfreier Zugriff in Java
• Realisiert durch die Klasse RandomAccessFile
• Kann für Lesen und Schreiben benutzt werden - implementiert
Schnittstellen DataInput und DataOutput
• Ähnelt FileInputStream und FileOutputStream, indem man ein
RandomAccessFile auf eine Datei öffnet und beim Erzeugen eines
von beiden als Parameter übergibt:
– den Dateinamen oder ein Datei-Objekt (File)
• Außerdem muss beim Erzeugen spezifiziert werden, ob die Datei nur
zum Lesen oder auch zum Schreiben geöffnet wird
• Man muss eine Datei lesen können, um auf sie schreiben zu können
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
66
Wahlfreier Zugriff in Java
• Erzeugen eines RandomAccessFile, um die Datei
“random.txt“ zu lesen:
new RandomAccessFile(“random.txt“, “r“);
• Erzeugen eines RandomAccessFile, um die Datei
“random.txt“ zu lesen und zu schreiben:
new RandomAccessFile(“random.txt“, “rw“);
• Nachdem die Datei geöffnet ist, kann mit den read und
write-Operationen gelesen bzw. geschrieben werden
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
67
Wahlfreier Zugriff in Java
• RandomAccessFile unterstützt einen Datei-Zeiger
• Ein Datei-Zeiger indiziert die aktuelle Dateiposition
• Beim Erzeugen ist der Datei-Zeiger 0 - der Anfang
der Datei
• Aufrufe von read und writeOperationen verschieben
den Datei-Zeiger automatisch
um die Anzahl der gelesenen
bzw. geschriebenen Bytes
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
68
Wahlfreier Zugriff in Java
Außer den üblichen I/O-Operationen, die den Zeiger
automatisch verschieben, unterstützt RandomAccessFile
drei Operationen zum expliziten Manipulieren des Zeigers:
• public int skipBytes(int n) throws IOException
• verschiebt den Zeiger vorwärts um n Bytes
• public native void seek(long pos) throws
IOException
• positioniert den Zeiger genau vor das spezifizierte Byte an der Stelle
pos
• public native long getFilePointer() throws
IOException
• gibt die aktuelle Position in der Datei zurück
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
69
Writer
Reader
StringWriter
StringReader
InputStreamReader
InputStreamReader
FilterReader
FilterReader
BufferedReader
BufferedReader
FileReader
PushbackReader
PushbackReader
LineNumberReader
LineNumberReader
OutputStreamWriter
OutputStreamWriter
FileWriter
FilterWriter
FilterWriter
BufferedWriter
BufferedWriter
PrintWriter
PrintWriter
Zeichen
Bytes
InputStream
FileInputStream
FilterInputStream
FilterInputStream
BufferedInputStream
BufferedInputStream
DataInputStream
DataInputStream
LineNumberInputStream
LineNumberInputStream
OutputStream
FileOutputStream
FilterOutputStream
FilterOutputStream
PushbackInputStream
PushbackInputStream
15. 7. 2003 © MM ...
GDI - Ein- und Ausgabe in Java
BufferedOutputStream
BufferedOutputStream
DataOutputStream
DataOutputStream
PrintStream
PrintStream
70
Herunterladen