W3docs

Wie man eine Datei in Java zeilenweise liest

Java-Datei zeilenweise lesen mit BufferedReader, Files.lines, Files.readAllLines und Scanner.

Eine Textdatei zeilenweise zu lesen ist eine der häufigsten Dateiaufgaben in Java. Das JDK bietet mehrere Möglichkeiten; die richtige Wahl hängt von der Dateigröße und davon ab, ob Sie eine einfache Schleife oder eine Stream-Pipeline bevorzugen. Dieses Kapitel zeigt die vier idiomatischen Ansätze und wann man welchen einsetzen sollte.

BufferedReader: das Streaming-Arbeitspferd

BufferedReader.readLine() liest pro Aufruf eine einzelne Zeile und gibt bei Dateiende null zurück, sodass es sich gut mit einer while-Schleife kombinieren lässt. Verwenden Sie try-with-resources, damit der zugrunde liegende Reader automatisch geschlossen wird:

Path file = Path.of("notes.txt");
try (BufferedReader br = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
  String line;
  while ((line = br.readLine()) != null) {
    System.out.println(line);
  }
}

Dieser Ansatz streamt die Datei: Es wird jeweils nur eine Zeile im Speicher gehalten, sodass sich auch ein mehrere Gigabyte großes Log-File problemlos verarbeiten lässt. Files.newBufferedReader verwendet standardmäßig UTF-8, aber die explizite Angabe des Zeichensatzes dokumentiert die Absicht und vermeidet plattformabhängige Dekodierung. Beachten Sie, dass readLine() den Zeilenabschluss (\n, \r oder \r\n) entfernt, sodass er im zurückgegebenen String nie erscheint.

Die try (...)-Form oben ist try-with-resources: Sie garantiert, dass der Reader auch dann geschlossen wird, wenn die Schleife eine Ausnahme auslöst. Lesen Sie Gepufferte Streams, um zu verstehen, warum das Einwickeln eines rohen Readers in einen BufferedReader für die Performance wichtig ist.

Files.lines: dieselbe Aufgabe als Stream

Wenn Sie eine funktionale Pipeline — filtern, abbilden, zählen — möchten, liefert Files.lines einen lazy Stream<String> über die Zeilen der Datei:

try (Stream<String> lines = Files.lines(Path.of("notes.txt"), StandardCharsets.UTF_8)) {
  lines.filter(s -> !s.isBlank())
       .map(String::trim)
       .forEach(System.out::println);
}

Wie BufferedReader liest es lazy und lädt nie die gesamte Datei. Der Haken ist, dass der Stream ein offenes Datei-Handle hält, also muss er geschlossen werden — verwenden Sie ihn immer innerhalb von try-with-resources, nie als bloßen Ausdruck. Wird das vergessen, entstehen Descriptor-Lecks.

Files.readAllLines: kleine Dateien, alles auf einmal

Wenn die Datei klein ist und Sie alle Zeilen vorab in einer List<String> haben möchten, ist Files.readAllLines die direkteste Option:

List<String> all = Files.readAllLines(Path.of("notes.txt"), StandardCharsets.UTF_8);
for (String line : all) {
  System.out.println(line);
}

Dieser Ansatz ist eager: Die gesamte Datei wird in den Speicher dekodiert, bevor Sie die erste Zeile berühren. Das ist praktisch für Konfigurationsdateien und Fixtures, aber ungeeignet für große Dateien — dort sollten Sie die Streaming-Ansätze bevorzugen. Scanner mit nextLine() und hasNextLine() ist eine vierte Option, die nützlich ist, wenn Sie auch Token parsen müssen, aber er ist langsamer und leicht zu missbrauchen, daher decken die drei oben genannten die meisten Fälle ab. Für einen umfassenderen Überblick über Datei-I/O — Öffnen, Lesen ganzer Dateien und die NIO Files-API — lesen Sie Dateien in Java lesen.

AnsatzSpeicherRückgabeGeeignet für
BufferedReader.readLine()Eine ZeileEinfache SchleifeGroße Dateien, manuelle Kontrolle
Files.lines()Eine Zeile (lazy)Stream<String>Pipelines auf großen Dateien
Files.readAllLines()Gesamte DateiList<String>Kleine Dateien, wahlfreier Zugriff
Scanner.nextLine()Eine ZeileEinfache SchleifeGemischtes Zeilen- und Token-Parsing

Arbeitsbeispiel: alle drei nebeneinander

Dieses Programm schreibt eine kleine temporäre Datei (damit es in sich geschlossen ist) und liest sie auf drei Arten — eine BufferedReader-Schleife, ein Files.lines-Stream und ein eageres Files.readAllLines:

java— editable, runs on the server

Was man aus dem Lauf mitnehmen kann:

  • Die BufferedReader-Schleife nummeriert vier Zeilen, und Zeile 3: [] zeigt, dass eine leere Zeile in der Datei als leerer String zurückgegeben wird, nicht übersprungen — readLine() meldet jede Zeile, einschließlich leerer.
  • readLine() hat jede Zeile ohne abschließendes \n ausgegeben, was bestätigt, dass es den Zeilenabschluss entfernt; die einzigen Klammern um den Text sind die wörtlichen [ und ], die der Code hinzugefügt hat.
  • Files.lines zählte non-blank lines: 3, weil der filter(s -> !s.isBlank()) die leere Zeile entfernte — die Stream-Pipeline arbeitet lazy über dieselben vier Zeilen, die die Schleife gesehen hat.
  • Files.readAllLines meldete total lines: 4 und first line : alpha, was beweist, dass es die gesamte Datei eagerly in eine List<String> geladen hat, die Sie mit get(0) indizieren können.
  • Jeder Reader befand sich innerhalb von try-with-resources (oder gab eine verwaltete List zurück), sodass das Datei-Handle und die temporäre Datei sauber freigegeben wurden, bevor done ausgegeben wurde — keine Descriptor-Lecks.

Übungen

Übung
Warum muss ein von Files.lines() zurückgegebener Stream innerhalb eines try-with-resources-Blocks verwendet werden?
Warum muss ein von Files.lines() zurückgegebener Stream innerhalb eines try-with-resources-Blocks verwendet werden?
Was this page helpful?