Kapitel 12 Dateiein- und -ausgabe Byte-Streams versus Character-Streams Die abstrakte Klasse Writer FileWriter StringWriter und CharArrayWriter BufferedWriter PrintWriter Die abstrakte Klasse Reader FileReader StringReader und CharArrayReader BufferedReader Datei- und Verzeichnis-Handling Programmierkurs Birgit Engels Anna Schulze Zentrum für Angewandte Informatik Köln WS 07/08 1 / 43 2 / 43 Allgemeine Konzepte Es gibt eine umfangreiche Bibliothek zum Zugriff auf Dateien und zur Verwaltung von Verzeichnissen. Die Klassen realisieren dabei das Konzept der Streams mit Hilfe objektorientierter Techniken. 12.1 Byte-Streams versus Character-Streams Die Klassen können vom Anwender erweitert werden Alle Klassen zur Dateiein-und -ausgabe befinden sich im Paket java.io. Um sie zu verwenden, muss am Anfang eines Programms import java.io.* aufgerufen werden 3 / 43 4 / 43 Byte-Streams versus Character-Streams Character-Streams Bis zur Version 1.0 des JDK gab es nur Byte-Streams Wesentliches Merkmal von Byte-Streams: Es wurden 8 Bit zur Speicherung eines Zeichens verwendet Eine Klasse für den lesenden Zugriff wird als Reader bezeichnet Java verwendet aber sonst 16 Bit lange Unicode-Zeichen Wesentliches Merkmal von Charakter-Streams: Es werden 16 Bit lange Unicode-Zeichen verwendet. Eine Klasse für den schreibenden Zugriff wird als Writer bezeichnet Dadurch wesentlich besser Kompatibilität zu String- und anderen Zeichentypen Es gibt Klassen, die Character-Streams in Byte-Streams überführen und umgekehrt Wir beschäftigen uns ausschließlich mit Character-Streams 5 / 43 6 / 43 Die abstrakte Klasse Writer Konstruktor (öffnet Ausgabestrom): protected Writer() Schließen des Ausgabestroms: public void close() throws IOException 12.2 Die abstrakte Klasse Writer Ein Zeichen schreiben: public void write(int c) throws IOException Mehrere Zeichen schreiben: public void write(char cbuf[]) throws IOException Ein String schreiben: public void write(String s) throws IOException 7 / 43 8 / 43 Abgeleitete Klassen FileWriter Zusätzliche Methoden der Klasse FileWriter: public FileWriter(String fileName) throws IOException public FileWriter(String fileName, boolean append) throws IOException public FileWriter(File file) throws IOException Es kann kein Objekt der abstraken Klasse Writer erstellt werden. Die abgeleiteten Klassen stellen eine Verbindung zu einem konkreten Ausgabegerät her und implementieren die Methoden der Klasse Writer: FileWriter: Ausgabe in eine Datei StringWriter: Writer zur Ausgabe in einen String Falls filename eine bereits vorhandene Datei bezeichnet, wird sie geöffnet und gelöscht. Andernfalls wird eine Datei dieses Namens angelegt und geöffnet. Wird der Parameter append mit dem Wert true übergeben, so wird die Datei nicht gelöscht. Im Abschnitt 12.11 gehen wir auf die Klasse File ein CharArrayWriter: Writer zur Ausgabe in ein Zeichen-Array BufferedWriter: Writer zur Ausgabepufferung PrintWriter: Ausgabe aller Basistypen im Textformat 9 / 43 10 / 43 StringWriter und CharArrayWriter FileWriter import java.io.*; public class MyFileWriter { public static void main(String[] args) { FileWriter f1; try { f1 = new FileWriter(‘‘hallo.txt’’, true); f1.write(‘‘hello\n’’); f1.close(); } catch(IOException e) { Im Gegensatz Klasse FileWriter schreiben StringWriter und CharArrayWriter ihre Ausgabe nicht in eine Datei, sondern in ein StringBuffer bzw. Character-Array: System.out.println(‘‘Fehler beim Erstellen der Datei’’); } } } 11 / 43 12 / 43 StringWriter StringWriter import java.io.*; public class MyStringWriter { public static void main(String[] args) { String hello; StringWriter s1; try { s1 = new StringWriter(); s1.write(‘‘Hello Java!’’); hello = s1.toString(); s1.close(); System.out.println(hello); } catch(IOException e) { Der Konstruktor legt einen StringBuffer an: public StringWriter() Zugriff auf den Inhalt des Puffers: public StringBuffer getBuffer() public String toString() System.out.println(‘‘Fehler beim Erstellen der Datei’’); } } 13 / 43 CharArrayWriter } 14 / 43 CharArrayWriter Der Konstruktor legt einen Character-Array an, das dynamisch wachsen kann: Leeren des Puffers: public CharArrayWriter() public void reset() Zugriff auf den Inhalt des Puffers: Größe des Puffers: public char[] toCharArray() public String toString() public int size() Übergabe an einen Writer: public void writeTo(Writer out) throws IOException 15 / 43 16 / 43 BufferedWriter BufferdWriter Die Klasse BufferedWriter hat die Aufgabe, Stream-Ausgaben zu puffern. An den Konstruktor wird ein externer Writer übergeben, an den die gepufferten Ausgaben weitergereicht werden: Dazu hat sie einen internen Puffer, in dem die Ausgaben von write zwischengespeichert werden public BufferedWriter(Write out) Wenn der Puffer voll ist oder die Methode flush aufgerufen wird, werden alle gepufferten Ausgaben in den echten Stream geschrieben. Zeilenschaltung: Das Puffern ist sinnvoll, wenn die Ausgabe in eine Datei geschrieben wird. public void newLine() Es reduziert sich die Anzahl der Zugriffe auf das externe Gerät. Die Performance wird erhöht. 17 / 43 BufferdWriter import java.io.*; public class MyBufferedWriter { public static void main(String[] args) { Writer f1; BufferedWriter f2; String s; try { ... } catch(IOException e) { ... } } } 18 / 43 BufferdWriter Writer f1; BufferedWriter f2; String s; try { f1 = new FileWriter(‘‘buffer.txt’’); f2 = new BufferedWriter(f1); for(int i=1; i<=10000;++i) { s = ‘‘Dies ist die ’’ + i + ‘‘. Zeile\n’’; f2.write(s); } f2.close(); f1.close(); } catch(IOException e) { System.out.println(‘‘Fehler beim Erstellen der Datei’’); } 19 / 43 20 / 43 PrintWriter PrintWriter An den Konstruktor wird ein externer Writer übergeben, auf den die Ausgaben umgeleitet wird: public PrintWriter(Writer out) Die Klasse PrintWriter hat die Aufgabe, primitive Datentypen in textueller Form auszugeben Die Ausgabe von primitiven Datentypen wird durch eine Reihe von Methoden mit dem Namen print bzw. println realisiert: public void print(boolean b) public void print(int i) ... 21 / 43 22 / 43 PrintWriter import java.io.*; public class MyPrintWriter { public static void main(String[] args) { PrintWriter f; int antwort = 42; try { f = new PrintWriter(new BufferedWriter( new FileWriter(‘‘test.txt’’))); f.print(‘‘Die Antwort ist: ’’); f.print(antwort); f.close(); } catch(IOException e) { 12.7 Die abstrakte Klasse Reader System.out.println(‘‘Fehler beim Erstellen der Datei’’); } } } 23 / 43 24 / 43 Die abstrakte Klasse Reader Die abstrakte Klasse Reader Konstruktor (öffnet Ausgabestrom): ready liefert true, falls der nächste Aufruf von read erfolgreich sein wird: public Reader() Schließen des Ausgabestroms: public boolean ready() public void close() Eine Anzahl von Zeichen im Lesestrom überspringen (Dabei kann es vorkommen, dass aus verschiedenen Gründen nicht exakt die angegebene Anzahl übersprungen werden kann Der Rückgabewert gibt die tatsächliche Anzahl an): Ein Zeichen lesen, das als int zurück gegeben wird: public int read() Mehrere Zeichen in Variable cbuf[] einlesen und Anzahl der gelesenen Zeichen zurück geben: public long skip(long n) public int read(char cbuf[]) 25 / 43 Abgeleitete Klassen 26 / 43 FileReader Es kann kein Objekt der abstraken Klasse Reader erstellt werden. Die abgeleiteten Klassen stellen eine Verbindung zu einem konkreten Einlesegerät her und implementieren die Methoden der Klasse Reader: Öffnen einer Datei: FileReader: Einlesen aus einer Datei public FileReader(String fileName) throws FileNotFoundException public FileReader(File file) throws FileNotFoundException StringReader: Reader zum Einlesen von Zeichen aus einem String CharArrayReader: Reader zum Einlesen von Zeichen aus einem Character-Array BufferedReader: Reader zur Eingabepufferung und zum Lesen von kompletten Zeilen LineNumberReader: Ableitung aus BufferedReader mit der Fähigkeit Zeilen zu zählen 27 / 43 28 / 43 StringReader und CharArrayReader StringWriter und CharArrayReader Der Konstruktoren: Im Gegensatz Klasse FileReader lesen StringReader und CharArrayReader aus einem String bzw. Character-Array: public StringReader(String s) public CharArrayReader(char buf[]) 29 / 43 BufferedReader 30 / 43 BufferdReader Die Klasse BufferedReader hat die Aufgabe, Stream-Eingaben zu puffern. An den Konstruktor wird ein externer Reader übergeben, an den die gepufferten Eingabe weitergereicht werden: Dazu hat sie einen internen Puffer, in dem die Eingabe von read zwischengespeichert werden public BufferedReader(Reader in) Wenn der Puffer voll ist oder die Methode flush aufgerufen wird, werden alle gepufferten Eingaben in den echten Stream geschrieben. Eine ganze Zeile einlesen: Das Puffern ist sinnvoll, wenn die Eingabe aus einer Datei gelesen wird. public String readLine() throws IOException Es reduziert sich die Anzahl der Zugriffe auf das externe Gerät. Die Performance wird erhöht. 31 / 43 32 / 43 LineNumber Reader Ableitung von BufferedReader Zählt die Anzahl der Eingabezeilen beim Einlesen Schnittstelle entspricht der von BufferedReader Zusätzliche Methoden 12.11 Datei- und Verzeichnis-Handling Zeilennummber einlesen: public int getLineNumber() Zeilenzähler verändern: public void setLineNumber(int lineNumber) 33 / 43 Die Klasse File 34 / 43 Die Klasse File Die Klasse File kann als Abstraktion eines Dateinamens angesehen werden. File kann absolute und relative Namen unter UNIX Windows repräsentieren. Ein File objekt kann auch Verzeichnisse repräsentieren. Konstruktoren import java.io.*; public class VerzeichnisHandling { public static void main(String[] args) { File testFile1 = new File(‘‘TestFile.txt’’); File testFile2 = new File(‘‘/beispiele/’’, ‘‘TestFile2.txt’’); } } Initialisierung eines File-Objekts zu dem angegebenen Datei- oder Verzeichnisnamen: public File(String pathname) Initialisierung, wenn der Verzeichnis- und Dateiname getrennt übergeben wird: public File(String parent, String child) public File(File parent, String child) 35 / 43 36 / 43 Verzeichnisnamen unter WINDOWS Zugriff auf Teile des Pfadnamens Wert der Variablen pathname bzw. child: public String getName() Unter WINDOWS ist der Backslash \ gleichzeitig Escape-Zeichen für Strings, deswegen muss er in Verzeichnisangaben doppelt angegeben werden. Wert der Variablen pathname bzw. parent+child: public String getPath() Absoluter Pfadname: new File(‘‘c:\\Java\\Beispiele\\Backslash.java’’); public String getAbsolutePath() Namen des Vaterverzeichnisses: public String getParent() 37 / 43 Zugriff auf Teile des Pfadnamens public class VerzeichnisHandling { public static void pfadAusgabe(File file) { System.out.println(file.getName()); System.out.println(file.getPath()); System.out.println(file.getAbsolutePath()); } public static void main(String[] args) { File testFile1 = new File(‘‘TestFile.txt’’); File testFile2 = new File(‘‘/beispiele/’’, ‘‘TestFile2.txt’’); pfadAusgabe(testFile1); pfadAusgabe(testFile2); } } 38 / 43 Zugriff auf Teile des Pfadnamens Ausgabe des Programms: TestFile.txt TestFile.txt /beispiele/TestFile.txt TestFile2.txt /beispiele/TestFile2.txt /beispiele/TestFile2.txt 39 / 43 40 / 43 Informationen über die Datei Informationen über die Datei Test der Existenz einer Datei: Würde das Objekt mit absoluter Pfadangabe konstruiert: public boolean exists() public boolean isAbsolute() Schreibender/Lesender Zugriffe möglich: Zeitpunkt der letzten Änderung in Millisekunden seit dem 1.1.1970 Mit Hilfe eines Date-Objektes kann Datum und Uhrzeit bestimmt werden: public boolean canWrite() public boolean canRead() Versteckte Datei: public long lastModified() public boolean isHidden() Anzahl der Zeichen in der Datei: Ist Objekt Datei oder Verzeichnis: public long length() public boolean isFile() public boolean isDirectory() 41 / 43 Zugriff auf Verzeichnisse Wurde ein File-Objekt für ein Verzeichnis konstruiert, so stehen weitere Methoden zur Verfügung. Inhalt des Verzeichnisses: public String[] list() Anlegen eines Verzeichnisses: public boolean mkdir() Löschen einer Datei oder Verzeichnisses: public boolean delete() 43 / 43 42 / 43