Writer - TU Darmstadt

Werbung
Grundzüge der Informatik
V12. 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
09.01.2003 © MM ...
GDI-1.12 - 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 Programmänderung und
Neuübersetzung
– Ungeeignet für größere oder komplexere Daten
– Keine Interaktionsmöglichkeit
– Ergebnisse sind nicht speicherbar
09.01.2003 © MM ...
GDI-1.12 - 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“)
09.01.2003 © MM ...
GDI-1.12 - 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
09.01.2003 © MM ...
GDI-1.12 - 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
09.01.2003 © MM ...
GDI-1.12 - 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 („Prozeßströme“)
09.01.2003 © MM ...
GDI-1.12 - 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
– Datensenkeströme / Prozeßströme
Klassenhierarchie
Klassenhierarchie
der
derZeichenströme
Zeichenströme
Klassenhierarchie
Klassenhierarchie
der
derByteströme
Byteströme
Klassen
Klassenfür
für
Datensenkeströme
Datensenkeströme
Klassen
Klassenfür
für
Prozeßströme
Prozeßströme
09.01.2003 © MM ...
GDI-1.12 - 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
• Prozeßströme
– Daten werden von anderen Strömen gelesen bzw.
auf andere Ströme geschrieben
– Daten werden nach dem Lesen, bzw. vor dem
Schreiben bearbeitet
09.01.2003 © MM ...
GDI-1.12 - 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
09.01.2003 © MM ...
GDI-1.12 - 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)
09.01.2003 © MM ...
GDI-1.12 - 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)
09.01.2003 © MM ...
GDI-1.12 - 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 löscht notfalls einen Strom,
auf den nicht mehr verwiesen wird
09.01.2003 © MM ...
GDI-1.12 - 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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
14
Hierarchie der Zeichenströme
Reader
StringReader
InputStreamReader
InputStreamReader
FilterReader
FilterReader
BufferedReader
BufferedReader
FileReader
PushbackReader
PushbackReader
LineNumberReader
LineNumberReader
Die Klassen in schattierten Kästchen sind Prozeßströme
09.01.2003 © MM ...
GDI-1.12 - 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 Prozeßströme
09.01.2003 © MM ...
GDI-1.12 - 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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
17
Hierarchie der Byteströme
InputStream
FileInputStream
FilterInputStream
FilterInputStream
BufferedInputStream
BufferedInputStream
DataInputStream
DataInputStream
LineNumberInputStream
LineNumberInputStream
PushbackInputStream
PushbackInputStream
Die Klassen in schattierten Kästchen sind Prozeßströme
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
18
Hierarchie der Byteströme
OutputStream
FileOutputStream
BufferedOutputStream
BufferedOutputStream
FilterOutputStream
FilterOutputStream
DataOutputStream
DataOutputStream
PrintStream
PrintStream
Die Klassen in schattierten Kästchen sind Prozeßströme
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
19
Nutzung der Klasse java.io.InputStream
• Objekte der Unterklassen von InputStream lesen
Bytes aus einer bestimmten Quelle (Datei, Netzwerk,...)
• Die wesentlichsten Methoden der Klasse sind:
– public abstract int read() throws IOException
• Liefert das nächste Byte im Strom (nur als 0-255)
• -1 falls keine weiteren Bytes vorhanden sind (End
of File, „EOF“)
• IOException bei allen anderen Problemen
– Z.B. Strom bereits geschlossen, Netzwerkverbindung verloren, ...
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
20
Nutzung der Klasse java.io.InputStream
• public abstract int read()
Datenquelle
Programm
InputStream
Datenquelle
Programm
InputStream
Datenquelle
-1
Programm
InputStream
09.01.2003 © MM ...
IOException
GDI-1.12 -
Ein- und Ausgabe in Java
Legende
Gelesene Bytes
Nächstes Byte
EOF
geöffneter
Strom
geschlossener
Strom
21
Nutzung der Klasse java.io.InputStream
• public int read(byte[] b) throws IOException,
NullPointerException
– liest eine Anzahl von Bytes und speichert sie in dem Byte-Array
b. Ruft read() wiederholt auf
– gibt Anzahl der gelesenen Bytes zurück bzw. -1, wenn kein Byte
gelesen werden konnte wegen EOF
– NullPointerException, wenn b == null
– IOException, wenn das erste Byte nicht gelesen werden
konnte aus irgendeinem anderen Grund als EOF
– I/O Ausnahmen während des Lesens von anderen Bytes werden
abgefangen (behandelt anstelle sie weiterzugeben) und die
Anzahl der Bytes soweit gelesen zurückgegeben
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
22
Nutzung der Klasse java.io.InputStream
Programm
Datenquelle
public int
read(byte[] b)
InputStream
Datenquelle
b
3
Programm
InputStream
b
-1
Programm
Datenquelle
InputStream
b
IOException
Programm
Datenquelle
InputStream
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
b
2
23
Nutzung der Klasse java.io.InputStream
• public long skip(long n) throws IOException
– versucht n Bytes von dem Eingabestrom zu überspringen
– Zurückgegeben wird die Anzahl übersprungener Bytes
– die Anzahl der tatsächlich übersprungenen Bytes kann kleiner als
n sein:
• EOF wurde erreicht bevor n Bytes übersprungen wurden
• IOException, wenn das erste Byte nicht gelesen werden
konnte aus irgendeinem anderen Grund als EOF
• public int available() throws IOException
– Gibt die Anzahl noch möglicher read()-Operationen aus
• public int close() throws IOException
– Schließt den Strom. Aus einem geschlossenen Strom kann nicht
mehr gelesen werden.
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
24
Nutzung der Klasse java.io.OutputStream
• OutputStream implementiert Byteströme, die AusgabeBytes erwarten und sie zu einer Senke schicken
• public abstract void write(int b) throws
IOException
– schreibt ein Byte (8 low-order Bits von b) auf die Senke
• public void write(byte[] b) throws IOException,
NullPointerException
– schreibt alle Bytes aus b sequentiell auf den Strom. Ruft
write(int) wiederholt auf.
• void write (byte[] b, int off, int len) throws
IOException, NullPointerException,
IndexOutOfBoundException
– Schreibt das gewählte Arraysegment durch mehrfaches
write(int) auf den Strom
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
25
Nutzung der Klasse java.io.OutputStream
• public void flush() throws IOException
– Alle vor dem Aufruf von flush() geschriebenen
Bytes, die eventuell innerhalb des Stroms gepuffert
sind, werden sofort auf die Senke des Stroms
geschrieben
– Da OutputStream selbst die Daten sofort auf die
Senke schreibt, hat der Aufruf von flush() auf einen
OutputStream keine Wirkung. Das kann für
Unterklassen von OutputStream anders sein.
• Für weitere Methoden siehe die Beschreibung in
der Java API Documentation
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
26
Datensenkeströme: Überblick
Senke Typ
Zeichenströme
Byteströme
Hauptspeicher
StringReader
StringWriter
StringBufferInputStream
Datei
FileReader
FileWriter
FileInputStream
FileOutputStream
StringBufferInputStream, StringReader und
StringWriter ermöglichen das Lesen von bzw. Schreiben
auf Strings im Hauptspeicher
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
27
Dateiströme
• Dateiströme stellen Eingabe-/Ausgabe-Ströme zur
Verfügung, deren Quellen/Senken Dateien im Dateisystem
sind:
– FileReader / FileWriter und
– FileInputStream / FileOutputStream
• 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)
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
28
Die Klasse FileInputStream
• FileInputStream stellt einen Byte-Eingabe-Datenstrom
dar, dessen Quelle eine Datei im Dateisystem ist
• public FileInputStream(String path) throws
SecurityException, FileNotFoundException
– Erzeugt einen neuen Dateilesestrom durch Öffnen der
benannten Datei
– FileNotFoundException, falls die Datei nicht existiert
– SecurityException, falls keine Leseberechtigung vorliegt
• Die Beschreibung passt auch auf die Varianten mit
Parameter java.io.File oder java.io.FileDescriptor
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
29
Die Klasse FileInputStream
Beispiel: Ausgeben eines Dateiinhalts auf den Bildschirm
import java.io.FileInputStream;
import java.io.IOException;
public class PrintFileFile {
public static void main(String[] args)
throws IOException {
FileInputStream in = new FileInputStream(args[0]);
int b;
while ((b = in.read()) != -1)
System.out.print(b);
in.close();
}
}
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
30
Die Klasse FileOutputStream
• FileOutputStream stellt einen Ausgabe-Strom dar, der in
eine Datei schreibt
• Beispiel: Die Zahlen von 1 bis 10 in eine Datei schreiben:
public void writeOneToTen(String filename)
throws IOException {
FileOutputStream out = new FileOutputStream(filename);
for (int i = 1; i <= 10; i++)
out.write(i);
out.close();
}
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
31
Prozeß-Ströme
Prozeß-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
• ...
Prozeß-Datenstrom Originaler Datenstrom
09.01.2003 © MM ...
Datensenke
GDI-1.12 - Ein- und Ausgabe in Java
Datenquelle
32
Prozeß-Ströme
Prozeß
Zeichenströme
Byteströme
Filterung
FilterReader,
FilterWriter
FilterInputStream,
FilterOutputStream
Pufferung
BufferedReader,
BufferedWriter
BufferedInputStream,
BufferedInputStream
Byte / Char
konvertieren
InputStreamReader,
OutputStreamWriter
Zeile zählen
LineNumberReader
LineNumberInputStream
DataInputStream,
DataOutputStream
Lesen von
Datentypen
Zurücklegen
PushbackReader
PushbackInputStream
Drucken
PrintWriter
PrintStream
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
33
FilterInput-/FilterOutputStream
• 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
• FilterInputStream und FilterOutputStream
sind Oberklassen aller Filter-Ströme
• Unterklassen von InputStream / OutputStream
• Ü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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
34
Die Klasse PrintStream
• Unterklasse von FilterOutputStream
• Erweitert einen anderen Ausgabestrom um
die Ausgabe einer String-Darstellung
verschieden-artiger Daten
• PrintStream löst nie Ausnahmen aus
• Stattdessen wird eine interne Markierung
gesetzt, die mit der Methode checkError()
abgefragt werden kann
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
35
Die Klasse PrintStream
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, long, ...)
• String
• sowie Object
• Standarddarstellung ist nicht „lesbar“
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
36
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 Ausgabe von
Daten an
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
37
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“
}
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
38
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)
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
39
Pufferströme
• Ein Datenstrom mit Pufferung kapselt einen anderen
Ursprungs-Datenstrom 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
• 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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
40
Pufferströme
• BufferedInputStream und BufferedOutputStream
bieten die Standard-Schnittstelle und
Implementierung für Byteströme mit Pufferung an
– Sie sind Unterklassen von FilterInputStream bzw.
FilterOutputStream
• BufferedReader und BufferedWriter bieten die
Standard-Schnittstelle und Implementierung für
Byte-Ströme mit Pufferung an
– Sie sind Unterklassen von Reader bzw. Writer.
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
41
Pufferströme
• BufferedOutputStream erweitert ByteAusgabeströme um die Fähigkeit, Daten zu puffern
• Bytes können geschrieben werden, ohne für jedes
Byte einen Zugriff auf dem unterliegenden Strom
starten zu müssen
• Beim Aufruf von einer Schreibe-Operation werden
die Daten erst auf dem internen Puffer gespeichert
• 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
•
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
42
Puffer-Ströme: BufferedOutputStream
Datensenke
Buffered
OutputStream
Programm
b
b
write
bbuf
bbuf
write
write bbuf
bbuf
write bbuf
bbuf
flush
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
43
Pufferströme
• BufferedInputStream erweitert Byte-Eingabeströme
mit Pufferung der Daten
• Bytes können von einem BufferedInputStream
gelesen werden, ohne für jedes zu lesendes Byte
einen Zugriff auf dem unterliegenden Stream starten
zu müssen
• Bei der ersten Lese-Operation werden so viele Daten
von dem unterliegenden Strom gelesen, bis der
Puffer voll ist
• Folgende Lese-Operationen lesen die Daten vom
Puffer und nicht vom System
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
44
Pufferströme: Beispiel
import java.io.FileInputStream;
import java.io.BufferedInputStream;
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]);
}
}
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
45
Pufferströme: Beispiel
public void testBuffer(String fname, int bufferSize)
{
try {
FileInputStream fis = new FileInputStream(fname);
BufferInputStream bis =
new BufferedInputStream(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());
}
}
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
46
Pufferströme: Beispiel
• Ergebnis für eine 650kB-Datei (x=Puffergröße, y=Zeit
[ms])
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
47
BufferedReader
Reader
BufferedReader
Reader()
read()
BufferedReader(Reader in)
BufferedReader(Reader in, int size)
readLine()
LineNumberReader
LineNumberReader(Reader in)
LineNumberReader(Reader in, int size)
getLineNumber()
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
48
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“
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
49
Pushback Strom: Illustration
Pushback
Datenquelle InputStream
Programm
read
unread
pushBack
read
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
50
Datenströme
• Daten-Ströme ermöglichen das bequeme Lesen
und Schreiben von primitiven Datentypen
(anstatt einzelner Zeichen)
• Ein DataInputStream liest Bytes von einer
Eingabe-Quelle 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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
51
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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
52
Datenströme: Beispiel
import
import
import
public
java.io.FileOutputStream;
java.io.DataOutputStream;
java.io.IOException
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();
} …
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
53
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 + ".");
}
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
54
Selbst definierte Prozeßströ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 FilterInputStream bzw.
FilterOutputStream erbt
• Es folgt ein Beispiel eines selbstdefinierten Filterstroms
auf Datenströmen, der alle Zeilen im unterliegenden
Strom herausfiltert, die einen bestimmten Substring
enthalten
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
55
Selbst definierte Prozeßströme
import java.io.DataInputStream;
import java.io.FilterInputStream;
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;
}
}
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
56
Selbst definierte Prozeßströ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); }
}
}
57
GDI-1.12 - Ein- und Ausgabe in Java
09.01.2003 © MM ...
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
• 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 '$', ';'
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
58
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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
59
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!
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
60
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!
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
61
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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
62
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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
63
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);
}
}
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
64
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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
65
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 sog. Dir-Eintrag (von Directory, engl.
Verzeichnis), in dem gespeichert wird, wo die einzelnen
Dateien innerhalb des Archivs anfangen
•
Aufgabe: eine bestimmte Datei aus dem Archiv extrahieren
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
66
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!
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
67
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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
68
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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
69
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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
70
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
09.01.2003 © MM ...
GDI-1.12 - Ein- und Ausgabe in Java
71
Herunterladen