Rheinwerk Computing < openbook >


 
Inhaltsverzeichnis
Materialien
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Arrays und ihre Anwendungen
5 Der Umgang mit Zeichenketten
6 Eigene Klassen schreiben
7 Objektorientierte Beziehungsfragen
8 Ausnahmen müssen sein
9 Geschachtelte Typen
10 Besondere Typen der Java SE
11 Generics<T>
12 Lambda-Ausdrücke und funktionale Programmierung
13 Architektur, Design und angewandte Objektorientierung
14 Java Platform Module System
15 Die Klassenbibliothek
16 Einführung in die nebenläufige Programmierung
17 Einführung in Datenstrukturen und Algorithmen
18 Einführung in grafische Oberflächen
19 Einführung in Dateien und Datenströme
20 Einführung ins Datenbankmanagement mit JDBC
21 Bits und Bytes, Mathematisches und Geld
22 Testen mit JUnit
23 Die Werkzeuge des JDK
A Java SE-Module und Paketübersicht
Stichwortverzeichnis


Download:

- Listings, ca. 2,7 MB


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 19 Einführung in Dateien und Datenströme
Pfeil 19.1 Alte und neue Welt in java.io und java.nio
Pfeil 19.1.1 java.io-Paket mit File-Klasse
Pfeil 19.1.2 NIO.2 und das java.nio-Paket
Pfeil 19.1.3 java.io.File oder java.nio.*-Typen?
Pfeil 19.2 Dateisysteme und Pfade
Pfeil 19.2.1 FileSystem und Path
Pfeil 19.2.2 Die Utility-Klasse Files
Pfeil 19.3 Dateien mit wahlfreiem Zugriff
Pfeil 19.3.1 Ein RandomAccessFile zum Lesen und Schreiben öffnen
Pfeil 19.3.2 Aus dem RandomAccessFile lesen
Pfeil 19.3.3 Schreiben mit RandomAccessFile
Pfeil 19.3.4 Die Länge des RandomAccessFile
Pfeil 19.3.5 Hin und her in der Datei
Pfeil 19.4 Basisklassen für die Ein-/Ausgabe
Pfeil 19.4.1 Die vier abstrakten Basisklassen
Pfeil 19.4.2 Die abstrakte Basisklasse OutputStream
Pfeil 19.4.3 Die abstrakte Basisklasse InputStream
Pfeil 19.4.4 Die abstrakte Basisklasse Writer
Pfeil 19.4.5 Die Schnittstelle Appendable *
Pfeil 19.4.6 Die abstrakte Basisklasse Reader
Pfeil 19.4.7 Die Schnittstellen Closeable, AutoCloseable und Flushable
Pfeil 19.5 Lesen aus Dateien und Schreiben in Dateien
Pfeil 19.5.1 Byteorientierte Datenströme über Files beziehen
Pfeil 19.5.2 Zeichenorientierte Datenströme über Files beziehen
Pfeil 19.5.3 Die Funktion von OpenOption bei den Files.newXXX(…)-Methoden
Pfeil 19.5.4 Ressourcen aus dem Modulpfad und aus JAR-Dateien laden
Pfeil 19.6 Zum Weiterlesen
 

Zum Seitenanfang

19.5    Lesen aus Dateien und Schreiben in Dateien Zur vorigen ÜberschriftZur nächsten Überschrift

Um Daten aus Dateien lesen oder in Dateien schreiben zu können, ist eine Stromklasse nötig, die es schafft, die abstrakten Methoden von Reader, Writer, InputStream und OutputStream auf Dateien abzubilden. Um an solche Implementierungen zu kommen, gibt es drei verschiedene Ansätze:

  • Die Utility-Klasse Files bietet einige newXXX(…)-Methoden, die uns Lese/Schreib-Datenströme für zeichen- und byteorientierte Dateien liefern.

  • Ein Class-Objekt bietet getResourceAsStream(…) und liefert einen InputStream, der Bytes aus Dateien im Modulpfad liest. Zum Schreiben gibt es nichts Vergleichbares. Falls Unicode-Zeichen gelesen werden sollen, muss der InputStream in einen Reader konvertiert werden.

  • Die speziellen Klassen FileInputStream, FileReader, FileOutputStream, FileWriter sind Stromklassen, die read(…)/write(…)-Methoden auf Dateien abbilden. So gibt es weitere Klassen für spezielle Quellen und Senken, etwa Netzwerkverbindungen.

Jede der Varianten hat Vor- und Nachteile. Wir wollen die einzelnen Möglichkeiten nun kennenlernen und voneinander abgrenzen.

 

Zum Seitenanfang

19.5.1    Byteorientierte Datenströme über Files beziehen Zur vorigen ÜberschriftZur nächsten Überschrift

Die Files-Klasse bietet Methoden, die direkt den Eingabe-/Ausgabestrom liefern. Beginnen wir mit den byteorientierten Stream-Klassen:

final abstract java.nio.file.Files
  • static OutputStream newOutputStream(Path path, OpenOption... options)

    throws IOException

    Legt eine Datei an und liefert den Ausgabestrom auf die Datei.

  • static InputStream newInputStream(Path path, OpenOption... options)

    throws IOException

    Öffnet die Datei und liefert einen Eingabestrom zum Lesen.

Da die OpenOption ein Vararg ist und somit weggelassen werden kann, ist der Programmcode kurz. (Er wäre noch kürzer ohne die korrekte Fehlerbehandlung …)

Beispiel: Eine kleine PPM-Grafikdatei schreiben

Das PPM-Format ist ein einfaches Grafikformat. Es beginnt mit einem Identifizierer, dann folgen die Ausmaße und schließlich die ARGB-Werte für die Pixelfarben.

Listing 19.8    src/main/java/com/tutego/insel/stream/WriteTinyPPM.java, main()

try ( OutputStream out = Files.newOutputStream( Paths.get( "littlepic.tmp.ppm" ) ) ) {

out.write( "P3 1 1 255 255 0 0".getBytes( StandardCharsets.ISO_8859_1 ) );

}

catch ( IOException e ) {

e.printStackTrace();

}

PPM-Dateien können online konvertiert werden, etwa mit https://convertio.co/de/ppm-jpg/.

 

Zum Seitenanfang

19.5.2    Zeichenorientierte Datenströme über Files beziehen Zur vorigen ÜberschriftZur nächsten Überschrift

Neben den statischen Files-Methoden newOutputStream(…) und newInputStream(…) gibt es zwei Methoden, die zeichenorientierte Ströme liefern, also Reader/Writer:

final abstract java.nio.file.Files
  • static BufferedReader newBufferedReader(Path path, Charset cs)

    throws IOException

  • static BufferedWriter newBufferedWriter(Path path, Charset cs, OpenOption... options)

    throws IOException

    Liefern einen Ein-/Ausgabestrom, der Unicode-Zeichen liest. Das Charset-Objekt bestimmt, in welcher Zeichenkodierung sich die Texte befinden, damit sie korrekt in Unicode konvertiert werden.

  • static BufferedReader newBufferedReader(Path path)

    throws IOException

    Entspricht newBufferedReader(path, StandardCharsets.UTF_8).

  • static BufferedWriter newBufferedWriter(Path path, OpenOption... options)

    throws IOException

    Entspricht Files.newBufferedWriter(path, StandardCharsets.UTF_8, options).

BufferedReader und BufferedWriter sind Unterklassen von Reader/Writer, die zum Zwecke der Optimierung Dateien im internen Puffer zwischenspeichern.

newBufferedWriter(…)

Die Rückgabe von newBufferedWriter(…) ist ein BufferedWriter, eine Unterklasse von Writer. Jeder Writer hat Methoden wie write(String), die Zeichenketten in den Strom schreiben. Die Methode soll das nächste Beispiel nutzen:

Listing 19.9    src/main/java/com/tutego/insel/stream/NewBufferedWriterDemo.java, main()

try ( Writer out = Files.newBufferedWriter( Paths.get( "out.bak.txt" ),

StandardCharsets.ISO_8859_1 ) ) {

out.write( "Zwei Jäger treffen sich ..." );

out.write( System.lineSeparator() );

}

catch ( IOException e ) {

e.printStackTrace();

}

newBufferedReader()

Der BufferedReader bietet neben den einfachen geerbten Lesemethoden der Oberklasse Reader zwei weitere praktische Methoden:

  • String readLine(): Liest eine Zeile bis zum Zeilenendezeichen (oder Stromende). Die Rückgabe ist null, wenn keine neue Zeile gelesen werden kann, weil das Stromende erreicht wurde. Das Zeilenendezeichen ist nicht Teil des Strings.

  • Stream<String> lines(): Liefert einen Stream von Strings, wobei jeder String eine Zeile (ohne den Trenner) repräsentiert.

So einfach ist ein Programm formuliert, das alle Zeilen einer Datei abläuft:

Listing 19.10    src/main/java/com/tutego/insel/stream/NewBufferedReaderDemo.java, main()

try ( BufferedReader in = Files.newBufferedReader( Paths.get( "lyrics.txt" ), 

StandardCharsets.ISO_8859_1 ) ) {

for ( String line; (line = in.readLine()) != null; )

System.out.println( line );

}

catch ( IOException e ) {

e.printStackTrace();

}
[zB]  Beispiel

Mit der Stream-API sieht es ähnlich aus – kurz skizziert:

try ( BufferedReader in = Files.newBufferedReader( … ) ) {

in.lines().forEach( System.out::println );

}

Falls es beim Lesen über den Stream zu einem Fehler kommt, wird eine RuntimeException vom Typ UncheckedIOException ausgelöst.

 

Zum Seitenanfang

19.5.3    Die Funktion von OpenOption bei den Files.newXXX(…)-Methoden Zur vorigen ÜberschriftZur nächsten Überschrift

Sofern eine Datei schon existiert, wird sie beim Öffnen zum Schreiben sozusagen gelöscht und dann neu beschrieben; existiert sie nicht, wird sie neu angelegt. Diese Standardoption ist aber ein wenig zu einschränkend, und daher beschreibt OpenOption Zusatzoptionen. OpenOption ist eine Schnittstelle, die von den Aufzählungen LinkOption und StandardOpenOption realisiert wird.

OpenOption

Beschreibung

java.nio.file.StandardOpenOption

READ

Öffnen für Lesezugriff

WRITE

Öffnen für Schreibzugriff

APPEND

Neue Daten kommen an das Ende. Atomar bei parallelen Schreiboperationen.

TRUNCATE_EXISTING

Für Schreiber: Existiert die Datei, wird die Länge vorher auf 0 gesetzt.

CREATE

Legt die Datei an, falls sie noch nicht existiert.

CREATE_NEW

Legt die Datei nur an, falls sie vorher noch nicht existierte. Andernfalls gibt es einen Fehler.

DELETE_ON_CLOSE

Die Java-Bibliothek versucht, die Datei zu löschen, wenn diese geschlossen wird.*

SPARSE

Hinweis an das Dateisystem, die Datei kompakt zu speichern, da sie aus vielen Null-Bytes besteht.**

SYNC

Jeder Schreibzugriff und jedes Update der Metadaten soll sofort zum Dateisystem.

DSYNC

Jeder Schreibzugriff soll sofort zum Dateisystem.

java.nio.file.LinkOption

NOFOLLOW_LINKS

Symbolischen Links wird nicht gefolgt.

* Das ist zum Beispiel dann praktisch, wenn der Hauptspeicher zu klein ist und Dateien zum Lesen/Schreiben als Zwischenspeicher verwendet werden. Am Ende der Operation kann die Datei gelöscht werden.

** Das ist für Windows und NTFS interessant, siehe auch https://stackoverflow.com/questions/17634362/what-is-the-use-of-standardopenoption-sparse.

Tabelle 19.3    Konstanten aus »StandardOpenOption« und »LinkOption«

Die Option CREATE_NEW kann nur funktionieren, wenn die Datei noch nicht vorhanden ist. Das zeigt anschaulich das folgende Beispiel:

Listing 19.11    src/main/java/com/tutego/insel/nio2/StandardOpenOptionCreateNewDemo.java, main()

Files.deleteIfExists( Paths.get( "opa.herbert.tmp" ) );

Files.newOutputStream( Paths.get( "opa.herbert.tmp" ) ).close();

Files.newOutputStream( Paths.get( "opa.herbert.tmp" ) ).close();

Files.newOutputStream( Paths.get( "opa.herbert.tmp" ),

StandardOpenOption.CREATE_NEW ).close();

Hier führt die letzte Zeile zu »java.nio.file.FileAlreadyExistsException: opa.herbert.tmp«.

Die Option DELETE_ON_CLOSE ist für temporäre Dateien nützlich. Das folgende Beispiel verdeutlicht die Arbeitsweise:

Listing 19.12    src/main/java/com/tutego/insel/nio2/StandardOpenOptionDeleteOnCloseDemo.java, main()

Path path = Paths.get( "opa.herbert.tmp" );



Files.deleteIfExists( path );

System.out.println( Files.exists( path ) ); // false



Files.newOutputStream( path ).close();

System.out.println( Files.exists( path ) ); // true



Files.newOutputStream( path, StandardOpenOption.DELETE_ON_CLOSE,

StandardOpenOption.SYNC ).close();

System.out.println( Files.exists( path ) ); // false

Im letzten Fall wird die Datei angelegt, ein Datenstrom geholt und gleich wieder geschlossen. Wegen StandardOpenOption.DELETE_ON_CLOSE wird Java die Datei von sich aus löschen, was Files.exists(Path, LinkOption...) belegt.

 

Zum Seitenanfang

19.5.4    Ressourcen aus dem Modulpfad und aus JAR-Dateien laden Zur vorigen ÜberschriftZur nächsten Überschrift

Im Klassen-/Modulpfad können neben den Klassendateien auch Ressourcen wie Grafiken oder Konfigurationsdateien enthalten sein. Der Zugriff auf diese Dateien wird nicht über Path oder File realisiert, denn die Dateien können sich innerhalb eines JAR-Archivs befinden. Daher gibt es am Class-Objekt getResourceAsStream(String):

final class java.lang.Class<T>

implements Serializable, GenericDeclaration, Type, AnnotatedElement
  • InputStream getResourceAsStream(String name)

    Gibt einen Eingabestrom auf die Datei mit dem Namen name zurück oder null, falls es keine Ressource mit dem Namen im Modulpfad gibt oder die Sicherheitsrichtlinien das Lesen verbieten.

[»]  Hinweis

Es geht bei der Methode nicht darum, eine Datei in einer anderen JAR-Datei zu öffnen und auszulesen.

Da der Klassenlader die Ressource findet, entdeckt er alle Dateien, die im Pfad des Klassenladers eingetragen sind. Das gilt auch für JAR-Archive, weil dort vom Klassenlader alles verfügbar ist. Da die Methode keine Ausnahme auslöst, ist ein Test auf die Rückgabe ungleich null unabdingbar.

Das folgende Programm liest ein Byte aus der Datei onebyte.txt ein und gibt es auf dem Bildschirm aus:

Listing 19.13    src/main/java/com/tutego/insel/io/stream/GetResourceAsStreamDemo.java

package com.tutego.insel.io.stream;



import java.io.*;

import java.util.Objects;



public class GetResourceAsStreamDemo {



public static void main( String[] args ) {

String filename = "onebyte.txt";

try ( InputStream is = Objects.requireNonNull(

GetResourceAsStreamDemo.class.getResourceAsStream( filename ),

"Datei gibt es nicht!" ) ) {

System.out.println( is.read() ); // 49

}

catch ( IOException e ) {

e.printStackTrace();

}

}

}

Die Datei onebyte.txt befindet sich im gleichen Pfad wie auch die Klasse, sie liegt also in com/tutego/insel/io/stream/onebyte.txt. Liegt die Ressource zum Beispiel im Wurzelverzeichnis des Pakets, lautet die Angabe "/onebyte.txt". Liegen die Ressourcen außerhalb des Modulpfades, können sie nicht gelesen werden. Der große Vorteil ist aber, dass die Methode alle Ressourcen anzapfen kann, die über den Klassenlader zugänglich sind, und das ist insbesondere der Fall, wenn die Dateien aus JAR-Archiven kommen – hier gibt es keinen üblichen Pfad im Dateisystem, er hört in der Regel beim JAR-Archiv selbst auf.

Um die getResourceAsStream(String)-Methoden nutzen zu können, ist ein Class-Objekt nötig, das wir in unserem Fall über Typname.class besorgen. Das ist nötig, weil unsere main(String[])-Methode statisch ist. Andernfalls kann innerhalb von Objektmethoden auch getClass() eingesetzt werden, eine Methode, die jede Klasse aus der Basisklasse java.lang.Object erbt.

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java ist auch eine Insel Java ist auch eine Insel

Jetzt Buch bestellen


 Buchempfehlungen
Zum Rheinwerk-Shop: Captain CiaoCiao erobert Java

Captain CiaoCiao erobert Java




Zum Rheinwerk-Shop: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Objektorientierte Programmierung

Objektorientierte Programmierung




 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2021

Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.

Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

[Rheinwerk Computing]



Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de



Cookie-Einstellungen ändern