Vorlesungsfolien (PDF, 24.01.2008)

Werbung
Vorlesung 11. Sitzung
Grundlegende
Programmiertechniken
Wintersemester 2007/2008
Dozent
Nino Simunic M.A.
Computerlinguistik, Campus DU
Grundlegende
Programmiertechniken, WS 2007/2008
Datenströme
Externe Informationen lesen/schreiben
Häufige Software-Operationen:
-3-
(Ein-)Lesen von Daten/Informationen aus einer
externen Quelle in die Java-Umgebung einlesen
Schreiben von Informationen aus dem Programm in ein
externes Ziel.
In allen Fällen sind E/A Datenströme die Basis.
Wichtige Merkmale der Ströme
Ein Strom kann die verschiedensten Typen von
Quellen und Zielen repräsentieren.
Ströme unterstützen verschiedene Typen von
Daten
-4-
Dateien, Geräte, andere Programme, Arrays im
Speicher, …
Bytes, primitive Datentypen, lokalisierte Zeichen,
Objekte, …
Einige Strom-Typen befördern Daten
ausschließlich, andere dagegen manipulieren sie
auf sinnvolle Weise.
Grundlage aller E/A Operationen: Lesen
In allen Fällen gilt beim Lesen von Daten:
-5-
Ein Strom ist eine Daten-Sequenz.
Ein Programm verwendet Input-Streams, um Daten
sequenziell/stückweise zu lesen.
Grundlage aller E/A Operationen: Schreiben
In allen Fällen gilt beim Schreiben von Daten:
-6-
Ein Strom ist eine Daten-Sequenz.
Ein Programm verwendet Output-Streams, um Daten
sequenziell/stückweise zu lesen.
Einleitung : E/A Ströme in Java (3)
Algorithmus der sequentiellen Datenverarbeitung
sowohl beim Lesen als auch beim Schreiben:
Lesen
Schreiben
Strom öffnen
Mehr Informationen?
Lese Informationen
Strom schließen
Strom öffnen
Mehr Informationen?
Schreibe Informationen
Strom schließen
Mehr Informationen?
Informationen? zu
zuverstehen
verstehenals
alsSchleife
Schleife
Mehr
mit Bedingung
BedingungSolange
SolangeInformationen
Informationenvorhanden
vorhanden
mit
-7-
Paket java.io
java.io enthält Strom-Klassen für die
Umsetzung von E/A Aktionen
Strom-Klassen sind grundlegend auf zwei
Klassenhierarchien verteilt:
Zeichen-Ströme:
»Textuelle« Informationen
Byte-Ströme:
Informationen als Rohdaten
-8-
Rohdaten-/Byte-Ströme
Byte-Strom
Klassen sind Ableitungen
von InputStream und OutputStream.
Typische
Anwendungen:
Lesen und Schreiben binärer Daten:
Bilder, Sounds, Objekte, …
-9-
Byte-Ströme: Einige Subklassen von InputStream und
OutputStream
abstract
Byte-Ströme:
Byte-Ströme:Öffnen/Lesen
Öffnen/Lesen
Byte-Ströme:
Byte-Ströme:Öffnen/Schreiben
Öffnen/Schreiben
InputStream
OutputStream
…
…
FilterInputStream
BufferedInputStream
FileInputStream
InflaterInputStream
FilterOutputStream
BufferedOutputStream
…
GZIPInputStream
ZipInputStream
Wahlfreie DV (vs. sequentielle DV)
-1010-
FileOutputStream
Printstream
…
Details der Byte-I/O Superklassen (1)
InputStream
…
InputStream
definiert
InputStream
definiert u.a.
u.a. Methoden
Methoden für
für das
das
FileInputStream
FilterInputStream
Lesen
Lesen von
von Bytes
Bytes und
und Byte-Arrays:
Byte-Arrays:
int
read()
int
read()
BufferedInputStream InflaterInputStream
int
int read(byte
read(byte cbuf[])
cbuf[])
… int read(byte cbuf[], int offset,
int read(byte cbuf[], int offset,
int
))
GZIPInputStream
ZipInputStream
int length
length
-1111-
Details der Byte-I/O Superklassen (2)
OutputStream
…
OutputStream
OutputStream definiert
definiert ähnliche
ähnliche Methoden
Methoden
FilterOutputStream
FileOutputStream
zum
zum Schreiben
Schreiben von
von Bytes:
Bytes:
int
write(int
c)
int
write(int
c)
BufferedOutputStream
int
int write(byte
write(byte cbuf[])
cbuf[])
int
cbuf[],
Printstream
int write(byte
write(byte
cbuf[], int
int
…
offset,
offset, int
int length)
length)
-1212-
Öffnen/Schließen von Streams
Alle Ströme werden bei ihrer Erzeugung
automatisch geöffnet
Ströme sollten stets nach erfolgter Verwendung via
close()-Methode geschlossen werden.
-1313-
Gilt für InputStream's, OutputStream's, aber auch
die noch folgenden Reader's, und Writer's
Der garbage collector schließt Ströme implizit, wenn ein
Strom-Objekt nicht mehr referenziert wird.
Verwendung von Byte-Strömen
Demonstration von Byte-Strömen anhand von E/A
Byte-Strömen fürs Lesen/Schreiben von Dateien.
Beispiel-Projekt:
-1414-
Andere Typen von Byte-Strömen werden fast analog
hierzu verwendet: Hauptunterschied ist die Art Ihrer
Erzeugung.
Kopiere Informationen aus einer Datei in eine andere
Verwendete Strom-Klassen im Projekt:
FileInputStream, FileOutputStream
CopyBytes.java
import java.io.*;
public class CopyBytes {
FileIS,FileOS
FileOS Konstruktoren:
Konstruktoren:
FileIS,
-Dateiname als
alsZeichenkette
Zeichenkette
-Dateiname
-Ein File-Objekt
File-Objekt(Hausaufgabe)
(Hausaufgabe)
-Ein
-Ein FileDescriptor
FileDescriptorObjekt
Objekt
-Ein
public static void main(String[] args) throws IOException {
FileInputStream in = null; FileOutputStream out = null;
try {
in = new FileInputStream("test.txt");
out = new FileOutputStream("test_copy.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
-1515-
Ströme und Ausnahmen
In vielen Fällen können Ströme Ausnahmen
auslösen
-1616-
Datei nicht gefunden/korrupt/schreibgeschützt,
Netzwerkverbindung down, …
In ebenso vielen Fällen verpflichten die I/O-Klassen
den Programmierer zur Ausnahmebehandlung
Die Ausnahmen sind ebenfalls in java.io zu finden.
Welche Ausnahme erzeugt werden kann, steht in
der Java-API, bzw. sagt Ihnen die IDE bzw. der
Compiler
Wann man Byte Ströme nicht verwenden sollten
CopyBytes funktioniert zwar (Text), stellt jedoch
low-level E/A dar, welches i.d.R. vermieden werden
sollte.
Nebenbei: Byte Ströme sind auf ISO-Latin-1 8-bit Bytes
begrenzt. Kann zu Problemen führen.
Idealfall ist die Nutzung entsprechender Ströme für
komplexe Daten. Hier also Zeichen-Ströme!
Noch einmal: Vermeiden Sie low-level E/A.
-1717-
test.txt enthält Zeichen-Daten, also komplexe Daten
Warum dann überhaupt ansehen? Alle anderen StromTypen haben Byte-Ströme als Grundlage! ;-)
Zeichen-Ströme (engl. character streams)
Im Allgemeinen ist E/A mit Zeichenströmen nicht
sonderlich komplizierter als mit Byte-Strömen.
Wichtiges Konzepte hier: Zeichenkodierungen.
-1818-
Java Plattform speichert Zeichen auf Unicode Basis
Internes Format wird automatisch übersetzt in/von der
lokalen Zeichencodierung
Man kann den Automatismus nutzen, oder aber
auch bequem eigene Zeichenkodierungen beim
Erzeugen der Ströme einfach angeben, falls
erforderlich!
Zeichen-Ströme
Reader und Writer sind abstrakte Superklassen
aller Zeichen-Ströme
Ihre Subklassen implementieren spezifische
Ströme für Datei E/A (analog zu den Byte-Strömen)
-1919-
Ströme, die direkt in data sinks (Datensenken)
lesen/schreiben (analog zum letzten Beispiel)
Filter-Ströme, die nicht direkt mit der Datensenke
verbunden sind und die Daten sinnvoll vor dem
Einlesen/Schreiben prozessiert
Zeichen-Ströme: Einige Subklassen von Reader und Writer
abstract
Zeichen-Ströme:Öffnen/
Zeichen-Ströme:Öffnen/Lesen
Lesen
Zeichen-Ströme:
Zeichen-Ströme:Öffnen/Schreiben
Öffnen/Schreiben
Reader
Writer
…
BufferedReader
InputStreamReader
…
…
LineNumberReader
FilterWriter
BufferedWriter
FileReader
OutputStreamWriter
…
FileWriter
-2020-
Details der Zeichen-I/O Superklassen (1)
Reader und InputStream definieren ähnliche
APIs, jedoch für unterschiedliche Datentypen:
-2121-
int read()
int read(char cbuf[])
int read(char cbuf[], int offset, int length)
Analog zu InputStream: Methoden zum
Flush'en, Überspringen, Markieren, Zurücksetzen
http://java.sun.com/javase/6/docs/api/java/io/Reader.html
abstract void close()
void mark(int readAheadLimit)
-2222-
Resets the stream.
long skip(long n)
Tells whether this stream is ready to be read.
void reset()
Tells whether this stream supports the mark() operation.
boolean ready()
Marks the present position in the stream.
boolean markSupported()
Closes the stream and releases any system resources
associated with it.
…
Skips characters.
Details der Zeichen-I/O Superklassen (2)
Writer und OutputStream ähneln sich ebenfalls in den
definierten Methoden
-2323-
write(char[] cbuf)
Writes an array of characters.
abstract void write(char[] cbuf, int off, int len)
Writes a portion of an array of characters.
void write(int c)
Writes a single character.
void write(String str)
Writes a string.
void write(String str, int off, int len)
Writes a portion of a string.
Wie gehabt: Öffnen der Ströme automatisch bei
Erzeugung des jeweiligen Strom-Objekts
Anwendungsdemonstration am Beispiel Datei-E/A
Analog zu Byte-Strömen: Klassen vorhanden,
welche Datei-E/A auf Zeichenstrom-Ebene
realisieren
Beispiel-projekt: CopyCharacters
-2424-
Gleiche Funktionalität wie CopyBytes.
Wichtigster Unterschied: Anstelle von FileInputStream
und FileOutputStream für E/A: FileReader,
FileWriter
CopyCharacters.java
import java.io.*;
public class CopyCharacters {
public static void main(String[] args) throws IOException {
FileReader inputStream = null; FileWriter outputStream = null;
try {
inputStream = new FileReader("test.txt");
outputStream = new FileWriter("test_copy2.txt");
int c;
while ((c = inputStream.read()) != -1) {
outputStream.write(c);
}
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
-2525}
Zeichen-Ströme, die Byte-Ströme verwenden
Zeichenströme sind oft "Wrapper" für Byte-Ströme
Der Zeichenstrom verwendet den Bytestrom für
physikalisches I/O
Der Zeichenstrom übernimmt die Aufgabe der
Übersetzung zwischen Zeichen und Bytes
FileReader z.B. verwendet FileInputStream
FileWriter verwendet FileOutputStream
Insgesamt zwei »general-purpose« Byte-zuZeichen »Brücken«-Ströme
-2626-
InputStream / Reader
OutputStream / Write
Zeilen-basiertes I/O
Zeichen E/A wird i.d.R. in größeren Einheiten (als
nur einzelne Zeichen) realisiert.
Eine typische Einheit: Zeile (eines Texts)
Beispiel: CopyLines
-2727-
Eine Zeichenkette, welcher mit einem Zeilenumbruch
endet.
Verwendet BufferedReader.readLine und
PrintWriter.println für zeilenweises E/A
CopyLines.java
import java.io.*;
public class CopyLines {
public static void main(String[] args) throws IOException {
BufferedReader inputStream = null;
PrintWriter outputStream = null;
try {
inputStream =
new BufferedReader(new FileReader("test.txt"));
outputStream =
new PrintWriter(new
FileWriter("text_out.txt"));
String l;
while ((l = inputStream.readLine()) != null) {
outputStream.println(l);
}
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
-2828}
Filterströme: Puffer-Ströme (engl. buffered streams)
Buffered I/O (gepuffertes E/A)
Um diesen Overhead zu vermeiden: Buffered I/O streams
-2929-
Means that each read or write request is handled directly by the
underlying OS.
Can make a program much less efficient, since each such
request often triggers disk access, network activity, or some
other operation that is relatively expensive.
Buffered input streams read data from a memory area known as
a buffer;
the native input API is called only when the buffer is empty.
Similarly, buffered output streams write data to a buffer, and the
native output API is called only when the buffer is full.
Buffered Streams (2)
Ein Programm kann einen ungepufferten Strom in
einen gepufferten Strom konvertieren
Ungepufferter Strom wird vom Puffer-Strom »verpackt«,
bzw. diesem als Objekt übergeben
Beispiel: CopyCharacters mit gepuffertem Strom
Einzige Änderung: Dem ungepuffertem wird ein
gepufferter Strom zwischengeschaltet.
Buffered Stream
BufferedReader
BufferedReader
new BufferedReader(…)
-3030-
Unbuffered Stream
FileReader
FileReader
new Filereader(…)
CopyCharacters2.java
import java.io.*;
public class CopyCharacters2 {
public static void main(String[] args) throws IOException {
BufferedReader
inputStream
= null;
BufferedWriter
outputStream
FileReader inputStream
= null;
FileWriter
outputStream
= null;=
null;
try {
inputStream
= new
FileReader("test.txt");
inputStream
= new
BufferedReader(new
FileReader("test.txt"));
BufferedWriter(new
FileWriter("test_copy.txt"));
outputStream
= new
outputStream
= new
FileWriter("test_copy2.txt");
int c;
while ((c = inputStream.read()) != -1) {
VieroutputStream.write(c);
gepufferte Stromklassen
Stromklassen zum
zum
Vier
gepufferte
}
Wrappen
ungepufferter Ströme:
Ströme:
Wrappen
ungepufferter
} finally {
if (inputStream != null)BufferedOutputStream
{
BufferedInputStream,
erstellten
BufferedInputStream,
BufferedOutputStream
erstellten
inputStream.close();
gepufferte
Byte-Ströme
gepufferte
Byte-Ströme
}
if (outputStream != null) {
BufferedReader
und
BufferedReader
undBufferedWriter
BufferedWritererzeugen
erzeugengepufferte
gepufferte
outputStream.close();
Zeichenströme
}
Zeichenströme
}
}
-3131}
Flush'en von gepufferten Strömen
It often makes sense to write out a buffer at critical points,
without waiting for it to fill.
Some buffered output classes support autoflush,
specified by an optional constructor argument.
When autoflush is enabled, certain key events cause the
buffer to be flushed.
-3232-
This is known as flushing the buffer.
For example, an autoflush PrintWriter object flushes the
buffer on every invocation of println or format.
To flush a stream manually, invoke its flush method.
The flush method is valid on any output stream, but has no
effect unless the stream is buffered.
E/A auf der Kommandozeile
-3333-
Programm verwenden häufig die Kommandozeile,
um mit dem Benutzer zu interagieren
Eine Möglichkeit der Umsetzung:
Verwenden von Standard-Strömen
Standard Streams (1)
Standard Streams sind Features von vielen
Betriebssystem.
Standardmäßig lesen Sie Input der Tastatur und schreiben
auf den Bildschirm
Die Java Plattform unterstützt drei Standard-Ströme:
Standard Input:
Standard Output:
Standard Error:
Zugriff via System.in
Zugriff via System.out
Zugriff via System.err
Standard Output, Standard Error sind Ausgabe-Ströme. Ihre jeweilige
Formatierung erlaubt jedoch, Fehler von regulären Ausgaben schnell
zu unterscheiden.
-3434-
Objekte dieser Ströme werden automatisch definiert und
müssen nicht geöffnet werden.
Standard Streams (2): Vorsicht
Aus historischen Gründen: Die Standardströme sind
keine Zeichen-Ströme, sondern Byte-Ströme
System.out und System.err werden als
PrintStream Objekte definiert.
Obwohl sie Byte-Ströme sind, verwendet PrintStream
einen eigenen internen Zeichen-Strom, um viele der
Features von Zeichen-Ströme zu emulieren.
Im Gegensatz dazu ist System.in ein Byte-Strom with
ohne Zeichenstrom-Funktionalität.
Um den Standard Input als Zeichenstrom zu nutzen, muss
System.in gewrappt werden: System.in in
InputStreamReader:
-3535-
InputStreamReader stdin = new
InputStreamReader(System.in);
Hausaufgabe
Kapitel»Scanning and Formatting« lesen, Quelltexte
implementieren, ausprobieren, erweitern.
-3636-
Download ab morgen auf unserer Seite
Evtl. Zusatzliteratur aus Head First Java
Übungsaufgaben nächste Woche setzen das Lesen des Kapitels
voraus. Fragen? Aufschreiben!
Wie immer: Auch Folien nachbereiten, Quelltexte
ausprobieren (auch die der Hausaufgaben-Kapitel),
erweitern, etc.
Next: Collections, Generezität, Ereignisbasierte
Programmierung, GUI-Entwicklung
Warnung: Evtl. müssen wir 2h in den Ferien machen
Referenzen
Sharon Zakhour, Scott Hommel, Jacob Royal,
Isaac Rabinovitch, Tom Risser, Mark Hoeber.
2006.The Java™ Tutorial Fourth Edition: A
Short Course on the Basics. Addison Wesley
Professional.
Java (1.6) API Dokumentation
-3737-
http://java.sun.com/javase/6/docs/api/
Herunterladen