1. Schreiben mit Überschreiben
Wenn Sie mit Dateien arbeiten, ist es wichtig zu verstehen, dass „in eine Datei schreiben“ nicht immer dasselbe bedeutet. Manchmal müssen Sie eine Datei vollständig überschreiben (zum Beispiel wenn Sie einen neuen Bericht speichern), und manchmal fügen Sie neue Informationen an das Ende einer bereits vorhandenen Datei an (zum Beispiel bei der Protokollierung einer Anwendung). In einigen Fällen müssen Sie den Inhalt der Datei einfach nur lesen, ohne sie zu ändern.
In Java (insbesondere im modernen Paket java.nio.file) wird der Modus der Dateiverarbeitung durch einen Satz spezieller Optionen bestimmt, die Sie an Schreibmethoden übergeben, zum Beispiel an Files.write(). Mit diesen Optionen können Sie ausdrücklich angeben, ob Sie eine Datei überschreiben oder etwas an ihr Ende anhängen möchten.
Wie das funktioniert
Wenn Sie Files.write(path, data) aufrufen, erstellt Java standardmäßig eine neue Datei oder überschreibt eine bereits vorhandene. Der gesamte alte Inhalt der Datei wird verworfen, und an seiner Stelle steht nur noch der neue Inhalt.
Dieses Standardverhalten ist eine Art „harter Neustart“ der Datei. Wenn die Datei zuvor 1000 Zeilen enthielt und Sie eine einzige Zeile schreiben, verschwindet alles Vorherige spurlos.
Beispiel: eine Zeichenkette in eine Datei schreiben
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class OverwriteFileExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
List<String> lines = List.of("Hallo, Welt!", "Dies ist ein neuer Eintrag in der Datei.");
// Wir schreiben Zeilen in die Datei (alter Inhalt wird gelöscht)
Files.write(path, lines);
System.out.println("Datei erfolgreich überschrieben!");
}
}
Nach dem Start dieses Codes verbleiben in der Datei myfile.txt nur die zwei Zeilen aus der Liste lines. Alles, was zuvor vorhanden war, verschwindet (ohne jede „Reue“ seitens Java).
2. Anhängen (append): Daten ans Ende der Datei hinzufügen
In manchen Aufgaben (zum Beispiel beim Führen eines Ereignisprotokolls, Logging, beim Sammeln von Daten) soll der alte Inhalt nicht zerstört, sondern neue Zeilen am Ende hinzugefügt werden. Im alten API verwendete man dafür häufig FileWriter mit dem Flag append = true. Im modernen API geht das einfacher und expliziter.
So geht’s
Alles, was Sie brauchen, ist die Option StandardOpenOption.APPEND an die Methode Files.write() zu übergeben:
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class AppendFileExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
List<String> moreLines = List.of("Noch eine Zeile hinzugefügt.", "Und noch eine!");
// Wir hängen Zeilen ans Ende der Datei an (alter Inhalt bleibt erhalten)
Files.write(path, moreLines, StandardOpenOption.APPEND);
System.out.println("Zeilen ans Dateiende angehängt!");
}
}
Wichtiger Punkt: Wenn die Datei nicht existiert, führt ein Anhängeversuch zu einem Fehler — Java erstellt die Datei im Modus APPEND nicht automatisch. Um die Datei zu erstellen, falls sie fehlt, verwenden Sie gleich zwei Optionen:
Files.write(path, moreLines, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
So sieht das in der Praxis aus
- Sie starten das Programm mit Schreiben (Überschreiben) — in der Datei stehen zwei Zeilen.
- Sie starten das Programm mit Anhängen — zu diesen Zeilen kommen neue hinzu, die alten bleiben bestehen.
3. Optionen kombinieren: CREATE, APPEND, TRUNCATE_EXISTING
In Java können mehrere Optionen kombiniert werden, um die Modi zum Öffnen einer Datei flexibler zu steuern:
- StandardOpenOption.CREATE — Datei erstellen, falls sie nicht existiert.
- StandardOpenOption.APPEND — Daten ans Ende anhängen.
- StandardOpenOption.TRUNCATE_EXISTING — Datei auf null Bytes kürzen (leeren), falls sie existiert.
- StandardOpenOption.CREATE_NEW — neue Datei erstellen; falls sie bereits existiert — Fehler.
Beispiel: Datei erstellen, falls sie fehlt, und Daten ans Ende anhängen
Files.write(path, moreLines, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
Beispiel: Datei erstellen oder vollständig leeren und neue Daten schreiben
Files.write(path, lines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
Tabelle: grundlegende Schreibmodi
| Option(en) | Verhalten |
|---|---|
| (standardmäßig) | Datei erstellen oder vorhandene überschreiben |
|
An das Ende anhängen, Fehler, wenn die Datei nicht existiert |
|
An das Ende anhängen, Datei erstellen, wenn sie nicht existiert |
|
Datei auf null Bytes kürzen und neue Daten schreiben |
|
Neue Datei erstellen, Fehler, wenn die Datei bereits existiert |
4. Binärdateien lesen und schreiben
Bisher haben wir mit Zeichenketten gearbeitet. Manchmal müssen jedoch „rohe“ Bytes geschrieben oder gelesen werden (zum Beispiel Bilder, Archive, PDF-Dateien). In diesem Fall verwenden Sie:
- Files.readAllBytes(path) — liest eine Datei in ein Byte-Array.
- Files.write(path, byteArray) — schreibt ein Byte-Array in eine Datei.
Beispiel: Datei kopieren
import java.nio.file.*;
import java.io.IOException;
public class CopyBinaryFileExample {
public static void main(String[] args) throws IOException {
Path source = Paths.get("logo.png");
Path target = Paths.get("logo_copy.png");
byte[] data = Files.readAllBytes(source);
Files.write(target, data);
System.out.println("Datei kopiert!");
}
}
Beispiel: Binärdaten anhängen
byte[] moreData = new byte[] {1, 2, 3, 4, 5};
Files.write(path, moreData, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
Achtung: Das Anhängen von Binärdaten an eine Datei, die bereits strukturierte Daten enthält (zum Beispiel ein Bild), führt in der Regel zu deren Beschädigung. Verwenden Sie Anhängen nur für Textdateien oder speziell vorbereitete Binärdateien (zum Beispiel Logs).
5. Wann benötigt man Streams (FileInputStream, BufferedReader u. a.)
Die Methoden Files.readAllBytes(), Files.write() sind praktisch für kleine und mittlere Dateien, wenn sich der gesamte Inhalt problemlos auf einmal in den Speicher laden lässt. Ist die Datei groß (zum Beispiel Gigabytes) oder möchten Sie zeilenweise lesen (zum Beispiel zur Analyse von Logs), verwenden Sie Streams.
Beispiel: Datei zeilenweise lesen mit BufferedReader
import java.nio.file.*;
import java.io.*;
public class ReadLinesExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Zeile: " + line);
}
}
}
}
Beispiel: Datei zeilenweise schreiben mit BufferedWriter und Anhängen
import java.nio.file.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class WriteAppendExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
try (BufferedWriter writer = Files.newBufferedWriter(
path,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND)) {
writer.write("Noch eine Zeile über BufferedWriter!");
writer.newLine();
}
}
}
6. Praxis: Datei erstellen und Zeilen anhängen
Verbinden wir alles zu einer Anwendung. Angenommen, wir haben ein Programm, das eine Aufgabenliste (To-do-Liste) in einer Textdatei führt. Jedes Mal, wenn der Benutzer eine neue Aufgabe hinzufügt, hängen wir sie ans Ende der Datei an.
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class TodoList {
public static void main(String[] args) throws IOException {
Path path = Paths.get("todo.txt");
Scanner scanner = new Scanner(System.in);
System.out.print("Neue Aufgabe eingeben: ");
String task = scanner.nextLine();
// Wir hängen die Aufgabe ans Dateiende an (erstellen die Datei, falls sie fehlt)
Files.write(path,
List.of(task),
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
System.out.println("Aufgabe hinzugefügt!");
}
}
Führen Sie das Programm mehrfach aus — jede Aufgabe erscheint in der Datei in einer neuen Zeile. Damit haben wir unsere erste „dauerhafte“ To-do-Liste!
7. Fehlerbehandlung: Was kann schiefgehen?
Die Arbeit mit Dateien ist immer mit Fehlerrisiken verbunden. Folgendes kann passieren:
- IOException — die Basisausnahme für alle Ein-/Ausgabefehler. Tritt zum Beispiel auf, wenn die Datei von einem anderen Programm belegt ist, die Schreib-/Leserechte fehlen, das Laufwerk voll ist usw.
- NoSuchFileException — wenn Sie versuchen, eine nicht vorhandene Datei zu lesen oder an sie anzuhängen (und die Option CREATE nicht angegeben haben).
- FileAlreadyExistsException — wenn Sie die Option CREATE_NEW verwenden, die Datei aber bereits existiert.
Empfehlung: Kapseln Sie die Dateiverarbeitung immer in try-catch:
try {
// Ihre Dateioperationen
} catch (IOException e) {
System.out.println("Fehler bei der Dateiverarbeitung: " + e.getMessage());
}
8. Typische Fehler beim Arbeiten mit Dateimodi
Fehler Nr. 1: Option CREATE beim Anhängen vergessen. Wenn Sie versuchen, mit APPEND an eine Datei anzuhängen, die noch nicht existiert, erhalten Sie eine NoSuchFileException. Fügen Sie immer CREATE hinzu, wenn die Datei automatisch erstellt werden soll.
Fehler Nr. 2: versehentliches Überschreiben der Datei. Der Aufruf Files.write(path, data) ohne zusätzliche Optionen löscht den gesamten alten Inhalt der Datei. Wenn Sie Daten hinzufügen möchten, ohne das Alte zu zerstören, verwenden Sie APPEND.
Fehler Nr. 3: Versuch, Binärdaten an eine Textdatei anzuhängen (oder umgekehrt). Wenn Sie Text- und Binärdaten in einer Datei mischen, erhalten Sie höchstwahrscheinlich eine unlesbare Datei. Verwenden Sie für eine Datei stets ein einheitliches Format.
Fehler Nr. 4: Stream nicht geschlossen. Wenn Sie Streams verwenden (BufferedWriter, BufferedReader), vergessen Sie nicht, sie zu schließen (am besten per try-with-resources), sonst bleibt die Datei möglicherweise gesperrt.
Fehler Nr. 5: Ausnahmen nicht behandelt. Jede Dateioperation kann eine IOException auslösen. Wenn Sie den Fehler nicht behandeln, wird das Programm abnormal beendet.
GO TO FULL VERSION