CodeGym /Kurse /JAVA 25 SELF /Textdateien lesen: zeilenweise oder komplett

Textdateien lesen: zeilenweise oder komplett

JAVA 25 SELF
Level 36 , Lektion 1
Verfügbar

1. Datei zeilenweise lesen: BufferedReader und Co.

Zuvor haben wir bereits das byteweise Lesen kennengelernt: Das ist praktisch für binäre Formate, für Text jedoch unhandlich. Eine Datei besteht aus Zeichen, die von der Kodierung abhängen. Deshalb bietet Java praktische Abstraktionen – FileReader, BufferedReader und andere Klassen, die einen Bytestrom in einen Zeichen- und Zeilenstrom verwandeln.

Stellen Sie sich eine Textdatei vor – sei es ein Programmlog, eine Benutzerliste oder sogar der große Roman „Krieg und Frieden“. Manchmal muss man die gesamte Datei schnell komplett lesen, manchmal zeilenweise durchgehen, und manchmal nur eine bestimmte Zeile herausziehen.

In Java gibt es mehrere Möglichkeiten, und die Wahl hängt von der Dateigröße und der Aufgabe ab. Wenn Sie die Datei zeilenweise verarbeiten müssen (z. B. die Zeilen zählen oder einen Eintrag finden), verwendet man das zeilenweise Lesen. Ist die Datei klein, kann man sie komplett in den Speicher laden und wie mit einer Liste von Zeilen arbeiten.

Warum ist zeilenweise lesen sinnvoll?

Bei großen Dateien bewahrt das zeilenweise Lesen vor Speicherproblemen. Einen Gigabyte-großen Log komplett in den Speicher zu laden, ist eine schlechte Idee: Die Wahrscheinlichkeit, auf OutOfMemoryError zu stoßen, ist hoch. Das Lesen Zeile für Zeile funktioniert hingegen mit geringen Ressourcen, selbst wenn die Datei hunderte Megabyte groß ist.

Wie macht man das in Java?

Die klassische Methode ist die Verwendung von BufferedReader (oder Verwandten) und der Methode readLine().

import java.io.*;

public class ReadLinesDemo {
    public static void main(String[] args) {
        String fileName = "example.txt";

        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            int lineNumber = 1;
            while ((line = reader.readLine()) != null) {
                System.out.printf("%3d: %s%n", lineNumber, line);
                lineNumber++;
            }
        } catch (IOException e) {
            System.out.println("Fehler beim Lesen der Datei: " + e.getMessage());
        }
    }
}

Hier öffnen wir die Datei zum Lesen und lesen mit readLine() Zeile für Zeile, bis wir das Ende erreichen (null). Danach geben wir jede Zeile mit ihrer Nummer aus.

Kurz zu try-with-resources

Sehen Sie die Konstruktion try (...) { ... }? Das ist try-with-resources. Sie garantiert, dass die Datei geschlossen wird, selbst wenn während des Lesens ein Fehler auftritt. Sie müssen close() nicht manuell aufrufen: Das Schließen geschieht automatisch, auch bei catch/finally.

Wozu dient BufferedReader?

BufferedReader liest nicht Zeichen für Zeichen, sondern in Blöcken (typischerweise je 8192 Byte), was die Arbeit mit Dateien beschleunigt. Außerdem bietet er die praktische Methode readLine(), die eine Zeile bis zum Zeilenumbruch zurückgibt.

Welche Puffergröße wählen?

Üblicherweise verwendet BufferedReader einen Puffer von 8192 Byte (8 KB) – das reicht für die meisten Aufgaben. Wenn Sie sehr lange Zeilen lesen (z. B. mit 100_000 Zeichen), kann man den Puffer vergrößern:

BufferedReader reader = new BufferedReader(new FileReader(fileName), 65536); // 64 KB Puffer

Für gewöhnliche Aufgaben ist die Standardgröße ideal.

2. Datei komplett lesen: Files.readAllLines und Files.readString

Ist die Datei klein (z. B. bis 1020 MB), lässt sie sich bequem komplett laden. Das ist praktisch, wenn man schnell eine Liste aller Zeilen benötigt, den Text analysieren oder ihn über das Netz versenden möchte.

Moderner Ansatz: Files.readAllLines

Seit Java 7 gibt es die praktische Klasse Files mit vielen nützlichen Methoden.

import java.nio.file.*;
import java.io.IOException;
import java.util.List;

public class ReadAllLinesDemo {
    public static void main(String[] args) {
        Path path = Path.of("example.txt");

        try {
            List<String> lines = Files.readAllLines(path);
            for (int i = 0; i < lines.size(); i++) {
                System.out.printf("%3d: %s%n", i + 1, lines.get(i));
            }
        } catch (IOException e) {
            System.out.println("Fehler beim Lesen der Datei: " + e.getMessage());
        }
    }
}

Hier gibt die Methode Files.readAllLines(path) eine Liste von Zeilen zurück (List<String>). Mit dieser Liste kann man wie mit einer normalen Collection arbeiten: suchen, sortieren, filtern.

Noch moderner: Files.readString (Java 11+)

Wenn Sie die gesamte Datei als einen String benötigen (z. B. für die Suche nach einer Teilzeichenfolge oder für die Übergabe an JSON), verwenden Sie Files.readString:

import java.nio.file.*;
import java.io.IOException;

public class ReadStringDemo {
    public static void main(String[] args) {
        Path path = Path.of("example.txt");

        try {
            String content = Files.readString(path);
            System.out.println("Dateiinhalt:");
            System.out.println(content);
        } catch (IOException e) {
            System.out.println("Fehler beim Lesen der Datei: " + e.getMessage());
        }
    }
}

Und was ist mit der Kodierung?

Standardmäßig verwenden die Methoden Files.readAllLines und Files.readString die Standardkodierung Ihres Systems. Wenn die Datei in einer anderen Kodierung gespeichert ist (z. B. Windows-1251), geben Sie sie explizit an:

List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
String content = Files.readString(path, StandardCharsets.UTF_8);

3. Ansätze im Vergleich: wann welchen verwenden

Methode Wann verwenden Vorteile Nachteile
BufferedReader.readLine()
Große Dateien, zeilenweise Verarbeitung Speichersparend, flexibel Nur zeilenweises Lesen
Files.readAllLines()
Kleine und mittlere Dateien Direkt eine Liste von Zeilen, einfach Für große Dateien – OutOfMemoryError
Files.readString()
Kleine Dateien, kompletter Text benötigt Gesamter Inhalt als ein String Keine Zeilenaufteilung

Empfehlung:
– Ist die Datei klein, verwenden Sie Files.readAllLines oder Files.readString.
– Ist die Datei groß oder kennen Sie ihre Größe nicht, verwenden Sie BufferedReader.readLine().

4. Praxisaufgaben: Beispiele und Erläuterungen

Beispiel 1. Zeilen in einer großen Datei zählen

Angenommen, Sie möchten wissen, wie viele Zeilen in einer sehr großen Datei stehen (z. B. einem Server-Log).

import java.io.*;

public class LineCount {
    public static void main(String[] args) {
        String fileName = "biglog.txt";
        int lineCount = 0;

        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            while (reader.readLine() != null) {
                lineCount++;
            }
            System.out.println("Gesamtzahl der Zeilen in der Datei: " + lineCount);
        } catch (IOException e) {
            System.out.println("Fehler beim Lesen der Datei: " + e.getMessage());
        }
    }
}

Beispiel 2. Zeilen nach Inhalt suchen

Wir suchen alle Zeilen, in denen das Wort „Fehler“ vorkommt (case-insensitive):

import java.io.*;

public class FindErrorLines {
    public static void main(String[] args) {
        String fileName = "biglog.txt";
        String keyword = "fehler";

        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            int lineNumber = 1;
            while ((line = reader.readLine()) != null) {
                if (line.toLowerCase().contains(keyword)) {
                    System.out.printf("%3d: %s%n", lineNumber, line);
                }
                lineNumber++;
            }
        } catch (IOException e) {
            System.out.println("Fehler beim Lesen der Datei: " + e.getMessage());
        }
    }
}

Beispiel 3. Konfiguration aus einer kleinen Datei laden

Datei config.txt:

host=localhost
port=8080
mode=dev

Wir lesen ihn komplett ein und zerlegen ihn in Schlüssel-Wert-Paare:

import java.nio.file.*;
import java.util.*;

public class ConfigLoader {
    public static void main(String[] args) throws Exception {
        Path path = Path.of("config.txt");
        List<String> lines = Files.readAllLines(path);

        Map<String, String> config = new HashMap<>();
        for (String line : lines) {
            if (line.trim().isEmpty() || line.startsWith("#")) continue; // Leere Zeilen und Kommentare überspringen
            String[] parts = line.split("=", 2);
            if (parts.length == 2) {
                config.put(parts[0].trim(), parts[1].trim());
            }
        }

        System.out.println("Geladene Konfiguration: " + config);
    }
}

5. Typische Fehler beim Lesen von Textdateien

Fehler Nr. 1: Versuch, eine Binärdatei als Textdatei zu lesen. Wenn Sie ein Bild oder Archiv mit BufferedReader oder Files.readAllLines öffnen, erhalten Sie unsinnige Zeichen und riskieren einen OutOfMemoryError. Für Binärdateien verwenden Sie InputStream!

Fehler Nr. 2: Ausnahme nicht behandelt. Dateien können gelöscht, verschoben oder gesperrt werden. Verpacken Sie das Lesen stets in try-catch und informieren Sie den Benutzer über Probleme.

Fehler Nr. 3: Kodierung ignoriert. Wenn Sie Text in einer bestimmten Sprache haben und die Datei ohne Angabe der Kodierung lesen, können „?????“ entstehen. Verwenden Sie StandardCharsets.UTF_8 oder die erforderliche Kodierung.

Fehler Nr. 4: Stream nicht geschlossen. Wenn die Datei nicht geschlossen wird, kann sie bis zum Programmende gesperrt bleiben. Verwenden Sie stets try-with-resources.

Fehler Nr. 5: Verwendung von read() statt readLine() für Textdateien. Die Methode read() liest Zeichen für Zeichen – das ist langsam und unpraktisch für Zeilen. Für Text verwenden Sie readLine() oder Methoden der Klasse Files.

1
Aufgabe
JAVA 25 SELF, Level 36, Lektion 1
Gesperrt
Zusammenführung der Fragmente einer großen Saga 📖
Zusammenführung der Fragmente einer großen Saga 📖
1
Aufgabe
JAVA 25 SELF, Level 36, Lektion 1
Gesperrt
Suche nach gemeinsamen Geheimnissen in Spionagedossiers 🕵️
Suche nach gemeinsamen Geheimnissen in Spionagedossiers 🕵️
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION