Informatik B Universität Osnabrück VL 14 1 Einführung VL 14 3 Für die Ein- und Ausgabeprogrammierung stehen Informatik B - Objektorientierte Programmierung in Java im Paket java.io unterschiedliche Klassen für Ein- bzw. Ausgabeströme sowie eine Reihe von Hilfsklassen zur Verfügung. Der Grundgedanke der Java-Datenströme ist die einheitliche Modellierung der Datenübermittlung zwischen Datenquelle und Datenspeicher (Datensenke), unabhängig davon, von welcher Art die Quelle bzw. der Speicherplatz sind (Lesen von Datei, Schreiben in einen Speicherbereich im Hauptspeicher, Lesen von Daten aus einer Quelle aus dem Internet etc.). Vorlesung 14: Sequentielle Ein-/Ausgabe, Ströme, Dateiorganisation © SS 2004 Prof. Dr. F.M. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Inhalt VL 14 2 Sequentielle Ein-/Ausgabe, Ströme, ¾ ¾ ¾ ¾ ¾ ¾ ¾ Einführung VL 14 4 Der Entwickler muss jeweils ein Objekt von der Dateiorganisation ¾ Informatik B Einführung Datenhaltung und Persistenz Standarddatenströme Eingabeströme Ausgabeströme Dateiorganisation Direktzugriffsspeicher (RandomAccessFile) Beispiele © Prof. Dr. Thiesing, FH Dortmund von ihm benötigten Stromklasse erzeugen und kann für die eigentliche Ein- oder Ausgabe die vordefinierten Operationen der Stromklassen anwenden. Das Schachteln von Strömen erlaubt die Konstruktion von Filtern, die bei der Ein- oder Ausgabe bestimmte Zusatzfunktionen ausführen. © Prof. Dr. Thiesing, FH Dortmund Informatik B Einführung VL 14 5 Die Stromklassen im Paket java.io lassen sich Datenhaltung und Ströme VL 14 7 Datenhaltung in Java ¾ Ein-/Ausgabe mit Hilfe von Strömen (streams) im JavaPaket java.io nach den folgenden Hauptmerkmalen einteilen: ¾ Informatik B Ein- und Ausgabe Strom Die wichtigste Unterscheidung betrifft den Unterschied zwischen Eingabe- und Ausgabeströmen, d.h. zwischen Stromklassen, die zum Einlesen von Daten aus einer Datenquelle verwendet werden und solchen, die der Ausgabe in einen Datenspeicher dienen. = Schnittstelle eines Programms nach außen mit einer Pipeline Vergleichbar } } } Auf der einen Seite Füllen der Pipeline mit Daten Auf der anderen Seite Entnehmen der Daten Verhalten ist analog zu einer Warteschlange Ströme } Ein Eingabestrom kann nicht zur Ausgabe benutzt werden und umgekehrt Klasse } sind immer unidirektional RandomAccessFile Ermöglicht das Lesen und Schreiben einer Datei © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Einführung ¾ VL 14 6 Binär- bzw. Zeichenströme Je nachdem, ob es sich um Datenströme vom Typ byte oder um Ströme auf der Basis von Zeichen (char) handelt, unterscheidet man Byteströme (Basisklassen InputStream und OutputStream) und Zeichenströme (Basisklassen Reader bzw. Writer). Unterscheidung zwischen Byte- und Zeichenströmen ist deshalb bedeutsam und notwendig, da Java auf UNICODE basiert und die Datentypen char und byte einen unterschiedlichen Darstellungsbereich haben (char ist ein 16 Bit-Datentyp und daher zwei Byte breit). Für die Umwandlung zwischen Byte- und Zeichenströmen stehen sog. Brückenklassen zur Verfügung (Klassen InputStreamReader und OutputStreamWriter). Informatik B Anwendung: Persistenz und Datenhaltung 8 Externe Speicher ¾ Aufbewahren der Zustände der Objekte über den aktuellen Programmlauf ¾ Persistenz Aus langfristig gespeicherten Daten kann wieder ein analoger Arbeitsspeicherzustand wie vor der Speicherung hergestellt werden Die © Prof. Dr. Thiesing, FH Dortmund VL 14 ¾ Drei verschiedene Möglichkeiten, Daten langfristig aufzubewahren Dateien objektorientierte relationale Datenbanken Datenbanken. © Prof. Dr. Thiesing, FH Dortmund Informatik B Standarddatenströme VL 14 9 Standarddatenströme ¾ Standardeingabestrom (System.in) Standardausgabestrom (System.out) Zeichen auf den Bildschirm aus System.out.println(...); Beispiel: ACHTUNG: ¾ System.out ist ein Bytestrom! Standardfehlerstrom (System.err) gibt Fehlermeldungen aus Beispiel: System.err.println(...); ¾ java.lang.System ermöglicht den Zugriff auf die Systemfunktionalität. © Prof. Dr. Thiesing, FH Dortmund dsc VL 14 11 die Quelle ein beliebiger Datenlieferant sein kann, etwa eine Datei im Dateisystem, eine Netzwerkverbindung, die über einen Port und ggf. ein bestimmtes Protokoll angesprochen wird (z. B. HTTP, FTP), ein String (also ein Zeichenkettenobjekt im Speicher) oder ein anderer Eingabestrom, der seine Daten an diesen Strom weiterreicht. Die Verkettung von Strömen ermöglicht es, mehrere Ströme, z.B. Dateien, zusammenzufassen und für den Aufrufer als einen einzigen Strom darzustellen. Bytes von der Standardeingabe ein Beispiel: System.in.read(); gibt Eingabeströme Eingabeströme lesen Daten aus einer Quelle ein, wobei liest ¾ Informatik B Standardausgabestrom © Prof. Dr. Thiesing, FH Dortmund Informatik B System.out.println(...) Persistenz und Datenhaltung in Java Standardfehlerstrom JavaProgramme VL 14 10 Informatik B Eingabeströme VL 14 12 System.err.println(...) Die beiden Basisklassen für Byte- und Stromkonzept in Java Tastatur Zeicheneingabeströme, InputStream und Reader haben weitgehend identische Operationen für das Einlesen von Daten und die Manipulation der Verbindung zur Datenquelle: Standardeingabestrom System.in.read() Standarddatenströme ¾ Dateien FileInputStream Ei n g ab e d a t ei e ¾ n FileOutputStream Au s g ab ed a Di r ¾ FilterInputStream FilterOutputStream - read()/int read() read(byte[] b)/int read(char[] cbuf) read(byte[] b, int off, int len) / read(char[] cbuf, int off, int len) Lesen eines einzelnen Bytes bzw. Zeichens oder eines Felds von Bytes bzw. Zeichen aus der Quelle Ein Rückgabewert von –1 zeigt das Ende des Eingabestroms an. n t ei e ek t z u g r i f f s d at ei en int int int int RandomAccessFile Dateiströme Filterströme © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Eingabeströme ¾ VL 14 13 long skip(long n) von der aktuellen Position in der Datenquelle um n Bytes oder Zeichen weiter 15 FileInputStream ByteArrayInputStream ob die Datenquelle das Setzen einer Marke unterstützt void mark(int MarkLimit) PipedInputStream setzt eine Marke, an die mit reset() zurückgesprungen werden kann, soweit nicht mehr als MarkLimit Byte oder Zeichen gelesen wurden ¾ VL 14 InputStream boolean markSupported() prüft, ¾ Eingabeströme Klassenhierarchie Byteeingabeströme springt ¾ Informatik B ObjectInputStream SequenceInputStream FilterInputStream void reset() setzt den "Lesekopf' an die Position der Marke in der Datenquelle zurück BufferedInputStream PushbackInputStream DataInputStream © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Eingabeströme ¾ VL 14 14 int available() liefert die Anzahl an Bytes, die ohne Blockieren mindestens gelesen werden können (nur bei InputStream) ¾ boolean ready() liefert true, falls der nächste Aufruf von read() ohne Blockieren erfolgen kann (nur bei Reader) ¾ void close() schließt den Datenstrom © Prof. Dr. Thiesing, FH Dortmund Informatik B Eingabeströme VL 14 16 Klasse Beschreibung InputStream Abstrakte Klasse für die byte-orientierte Eingabe FileInputStream Eingabe von Datei ByteArrayInputStream Eingabe aus Byte-Array PipedInputStream Eingabe von Pipe (Nachrichtenwarteschlange zur Kommunikation zwischen Threads) ObjectInputStream Eingabe serialisierter Objekte SequenceInputStream Verkettung mehrerer Eingabeströme zu einem Strom FilterInputStream Abstrakte Klasse für die gefilterte Eingabe BufferedInputStream Eingabe mit Pufferung PushbackInputStream Eingabe mit der Möglichkeit zur Rückstellung von gelesenen Bytes DataInputStream Eingabe von primitiven Datentypen in portabler Weise © Prof. Dr. Thiesing, FH Dortmund Informatik B Eingabeströme VL 14 17 Informatik B Eingabeströme VL 14 19 Zu den Operationen von InputStream bzw. Klassenhierarchie Zeicheneingabeströme Reader kommen für einzelne Stromklasse spezifische Operationen hinzu, die die besondere Funktionalität dieses Stromtyps ausmachen. Beispielsweise verfügt BufferedReader als Unterklasse von Reader zusätzlich über eine Operation readLine(), die eine ganze Zeile aus einer Datenquelle (z.B. eine Textdatei, die über einen FileReader angesprochen wird) einliest. Reader InputStreamReader FileReader CharArrayReader StringReader PipedReader BufferedReader LineNumberReader FilterReader PushbackReader © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Eingabeströme VL 14 18 Klasse Beschreibung Reader Abstrakte Klasse für die zeichen-orientierte Eingabe InputStreamReader Klasse für das Interpretieren eines Byte-Stroms als ZeichenStrom FileReader Eingabe von Datei CharArrayReader Eingabe aus Zeichen-Array StringReader Eingabe aus Zeichenkette PipedReader Eingabe von Pipe (Nachrichtenwarteschlage zur Kommunikation zwischen Threads) BufferedReader Eingabe mit Pufferung und Eingabe ganzer Zeilen LineNumberReader Eingabe mit der Fähigkeit, Zeilen zu zählen FilterReader Abstrakte Klasse für gefilterte Eingabe PushbackReader Eingabe mit der Möglichkeit zur Zurückstellung von Zeichen Informatik B Eingabeströme 20 Schachteln von Eingabeströmen ¾ Das Schachteln von Eingabeströmen bietet die Möglichkeit, bei der Eingabe bestimmte Zusatzfunktionen und Filter zu realisieren. ¾ Zum Schachteln von Eingabeströmen wird dem Konstruktor z.B. der Klasse BufferedReader ein Objekt der Klasse FileReader übergeben: BufferedReader r = new BufferedReader( new FileReader(“MeinText.txt“)); ¾ © Prof. Dr. Thiesing, FH Dortmund VL 14 Eine Leseoperation des BufferedReader ruft dann eventuell zunächst die Leseoperation des FileReader auf, um den Puffer aufzufüllen und liefert anschließend die Daten als Ergebnis zurück. © Prof. Dr. Thiesing, FH Dortmund Informatik B Eingabeströme VL 14 21 Verketten von Eingabeströmen ¾ Mit Hilfe der Klasse SequenceInputStream können mehrere Eingabeströme zusammengefasst werden, so dass sie wie ein einzelner Strom erscheinen. ¾ Die Daten werden nacheinander aus den einzelnen Strömen gelesen. ¾ Die Ströme können z.B. in Form einer Enumeration oder - wenn es sich um genau zwei Ströme handelt direkt an den Konstruktor übergeben werden: Informatik B Ausgabeströme 23 Die beiden abstrakten Basisklassen für die Datenausgabe, OutputStream und Writer, haben annähernd dieselben Operationen: ¾ ¾ ¾ public SequenceInputStream(Enumeration e) public SequenceInputStream(InputStream s1, InputStream s2) void write(int b)/void write (int c) void write(byte[] b)/void write(char[] cbuf) void write(byte[] b, int off, int len)/ void write(char[] cbuf, inf off, int len) Schreiben eines einzelnen Bytes bzw. Zeichens oder eines Feldes von Bytes/Zeichen in den Ausgabestrom © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Ausgabeströme VL 14 VL 14 22 Ausgabeströme dienen dazu, die an sie übergebenen Daten in einen Datenspeicher zu schreiben. Analog zu den Eingabeströmen können die Datenspeicher unterschiedlicher Art sein (Datei, Zeichenkette im Speicher, Netzwerkverbindung, ein anderer Ausgabestrom etc.). Informatik B Ausgabeströme ¾ 24 void flush() schreibt noch im Puffer des Stroms verbliebene Daten in den Datenspeicher (nur bei gepufferten Strömen relevant) ¾ void close() schließt den Ausgabestrom Die Klasse Writer besitzt zusätzlich folgende Schreiboperationen: ¾ ¾ © Prof. Dr. Thiesing, FH Dortmund VL 14 void write(String str) void write(String str, int off, int len) © Prof. Dr. Thiesing, FH Dortmund Informatik B Ausgabeströme VL 14 25 Klassenhierarchie Byteausgabeströme Informatik B Ausgabeströme VL 14 27 Klassenhierarchie Zeichenausgabeströme OutputStream Writer FileOutputStream OutputStreamWriter ByteArrayOutputStream FileWriter PipedOutputStream CharArrayWriter ObjectOutputStream StringWriter FilterOutputStream PipedWriter BufferedOutputStream BufferedWriter DataOutputStream PrintWriter FilterWriter © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Ausgabeströme VL 14 26 Informatik B Ausgabeströme VL 14 28 Klasse Beschreibung Klasse Beschreibung OutputStream Abstrakte Klasse für die byte-orientierte Ausgabe Writer Abstrakte Klasse für die zeichen-orientierte Ausgabe FileOutputStream Ausgabe in eine Datei OutputStreamWriter ByteArrayOutputStream Ausgabe in ein Byte-Array Klasse für die Umformung eines Zeichenstroms in einen Bytestrom PipedOutputStream Ausgabe in eine Pipe (Nachrichtenwarteschlange zur Kommunikation zwischen Threads) FileWriter Ausgabe in eine Datei CharArrayWriter Ausgabe in ein Zeichen-Array ObjectOutputStream Ausgabe serialisierter Objekte StringWriter Ausgabe in eine Zeichenkette FilterOutputStream Abstrakte Klasse für die gefilterte Ausgabe PipedWriter BufferedOutputStream Ausgabe mit Pufferung Ausgabe in eine Pipe (Nachrichtenwarteschlage zur Kommunikation zwischen Threads) DataOutputStream Ausgabe von primitiven Datentypen in portabler Weise BufferedWriter Ausgabe mit Pufferung PrintWriter Ausgabe aller Datentypen im Textformat FilterWriter Abstrakte Klasse für die gefilterte Ausgabe © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Ausgabeströme VL 14 29 Beim Öffnen einer existierenden Datei zum Schreiben besteht die Möglichkeit, zwischen dem Überschreiben und dem Anhängen an die vorhandenen Daten zu wählen. Wie für die Eingabeströme gilt, dass die konkreten Unterklassen von OutputStream bzw. Writer die Operationen der Basisklassen überschreiben bzw. spezifische Operationen hinzufügen. ¾ Beispielsweise verfügt BufferedWriter über eine Operation newLine(), mit der ein Zeilenumbruch in den Ausgabestrom geschrieben werden kann. Informatik B Dateiorganisation benötigt, dann können die üblichen Verfahren der Dateiorganisation verwendet werden Die grundlegenden Speicherungsformen sind: ¾ Sequentielle Speicherung ¾ Indexsequentielle Speicherung ¾ Indizierte Organisation mit physisch logisch ¾ sortiertem Index sortiertem Index Hash-Verfahren. © Prof. Dr. Thiesing, FH Dortmund Informatik B VL 14 30 DateiEingabe.java 31 Wird kein Datenbanksystem für die Speicherung © Prof. Dr. Thiesing, FH Dortmund Beispiele VL 14 Informatik B Dateiorganisation VL 14 32 Physische Sortierung ¾ Reihenfolge der Speicherung entspricht der Sortierreihenfolge ¾ Umfangreiche Daten: Nicht mehr alle Daten können gleichzeitig im Arbeitsspeicher aufbewahrt werden ¾ Trotzdem: Direkter Zugriff auf die auf einem Langfristspeicher abgelegten Datensätze. DateiAusgabe.java © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Dateiorganisation VL 14 33 Informatik B Dateiorganisation ¾ random access ¾ Fast alle Programmiersprachen unterstützen eine Direktzugriffspeicherungsform ¾ Ermöglicht ohne Durchsuchung der Datei von vorne nach hinten Verwaltung der Zuordnung zwischen einem solchen Schlüssel und einer zugehörigen Datensatzposition in einer Tabelle Indextabelle beansprucht nur wenig Platz } } von Datensätzen Lesen und erneutes Speichern eines Datensatz Sie kann komplett in den Arbeitsspeicher geladen werden Sie wird aber selbst ebenfalls in einer Datei gespeichert und bei Änderungen aktualisiert. Voraussetzung für diese Speicherungsform: Alle ¾ 35 Indexverwaltung Speicherung ¾ VL 14 Datensätze haben dieselbe Länge Zugriff: Positionierung eines Zeigers auf den Anfang des gewünschten Datensatzes. © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Dateiorganisation ¾ VL 14 34 Endbenutzer weiß nicht, an welcher Position ein Datensatz beginnt Herstellung und Verwaltung einer Zuordnung zwischen einem fachkonzeptorientierten Schlüssel und der Position des Datensatzes ¾ Im kaufmännischen Bereich: Verwendung } von Nummern für die Identifikation Z.B. Kundennummer, Artikelnummer usw. Informatik B Dateiorganisation ¾ © Prof. Dr. Thiesing, FH Dortmund 36 Indizierte Organisation mit physisch sortiertem Index Indextabelle Datensatztabelle 0 -1 0 1 5 100 2 10 200 3 7 4 8 400 5 3 500 6 7 600 7 -1 700 8 -1 800 9 -1 900 Schlüssel z.B. Kundennr. VL 14 Aktuell Index z.B. Nummer des Datensatzes 300 Datensätze »ProfiSoft...« Beginn Satzlänge der z.B. 100 Bytes Datensätze: Nummer des Datensatzes * Satzlänge © Prof. Dr. Thiesing, FH Dortmund Informatik B Dateiorganisation VL 14 37 Direktzugriffsspeicher in Java 1. Datei – MAX: int – Dateinname: String – Indextabelle[ ]: int – Aktuell: int – Dateiname: String – SATZLAENGE: int + + + + + – + + + + + + – – + Index() erzeugeEintrag(Schluessel: int, Index: int): void gibIndexZuSchluessel(Schluessel: int): int ladeIndexDatei(): void speichereIndexDatei(): void aktualisiereIndexDatei(Schluessel: int): void VL 14 39 RandomAccessFile ¾ RandomAccessFile(String name,String mode) Klassen für eine Indexverwaltung: Beispiel Index Informatik B 2. ¾ Datei() speichereSatz(Satz: String, Index: int): void leseSatz(Index: int): String oeffneDatei(Name: String): void schliesseDatei(): void gibAnzahlDatensaetze(): int positioniereAufSatz(Index: int): void readFixedString(Laenge: int): String writeFixedString(einDatensatz: String, Laenge: int): void Parameter: systemabhängiger Dateiname Parameter: “r” oder “rw” public void writeInt(int) Schreibt ¾ public int readInt() Liest ¾ eine 32-Bit-lange ganze Zahl von der Datei public void writeChar(int) Schreibt ¾ ein int in die Datei (jeweils 4 Bytes) 1 Zeichen im Unicode (2 Bytes) in die Datei public char readChar() Liest ein Unicode-Zeichen von der Datei. © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund Informatik B Dateiorganisation ¾ ¾ ¾ VL 14 38 Klassen sind unabhängig voneinander Indexverwaltung kann ausgetauscht oder erweitert werden, ohne Änderung der Klasse Datei Beispiel Verwendung } Informatik B Direktzugriffsspeicher in Java ¾ 40 public void seek(long pos) Positionszeiger wird auf den zu lesenden Satz gestellt Ab dieser Position wird dann mit den Lese- und Schreiboperationen gelesen bzw. Geschrieben pos gibt dabei die Byte-Position an, d.h. die Länge der Datensätze wird in bytes gezählt des Kundennamens zusätzlich als Schlüssel Eine weitere Indexklasse kann mit Hilfe einer Hash-Tabelle eine Zuordnung zwischen Kundenname und Datensatzposition verwalten. VL 14 ¾ public int skipBytes(int n) Überspringen ¾ von n Bytes public long length() Gibt die Länge der Datei zurück Beispiel: Klasse Direktzugriffsspeicher ¾ Klasse Index / Klasse Datei. © Prof. Dr. Thiesing, FH Dortmund © Prof. Dr. Thiesing, FH Dortmund