CodeGym /Kursy /JAVA 25 SELF /Odczyt plików tekstowych: wiersz po wierszu, w całości

Odczyt plików tekstowych: wiersz po wierszu, w całości

JAVA 25 SELF
Poziom 36, Lekcja 1
Dostępny

1. Odczyt pliku wiersz po wierszu: BufferedReader i spółka

Wcześniej poznaliśmy odczyt bajt po bajcie: jest wygodny do pracy z formatami binarnymi, lecz dla tekstu takie podejście bywa kłopotliwe. Plik składa się z znaków, które zależą od kodowania. Dlatego Java oferuje wygodne „nakładki” — FileReader, BufferedReader i inne klasy, które zamieniają strumień bajtów na strumień znaków i wierszy.

Wyobraź sobie plik tekstowy — czy to log działania programu, listę użytkowników, czy nawet ogromną powieść „Wojna i pokój”. Czasem trzeba szybko przeczytać cały plik w całości, czasem — przejść po nim wiersz po wierszu, a innym razem — wyciągnąć jeden konkretny wiersz.

W Javie jest kilka sposobów, by to zrobić, a wybór zależy od rozmiaru pliku i zadania. Jeśli trzeba przetworzyć plik po jednym wierszu (np. zliczyć liczbę wierszy lub znaleźć wpis), stosujemy odczyt wierszowy. A jeśli plik jest niewielki — można wczytać go w całości do pamięci i pracować z nim jak z listą wierszy.

Dlaczego odczyt wierszami — to dobry pomysł?

Dla dużych plików odczyt linia po linii chroni przed problemami z pamięcią. Wczytywanie do pamięci gigabajtowego logu to zły pomysł: duże ryzyko natrafienia na OutOfMemoryError. Natomiast odczyt wiersz po wierszu pozwala działać bez większych kosztów, nawet jeśli plik waży setki megabajtów.

Jak robi się to w Javie?

Najbardziej klasyczny sposób — użyć BufferedReader (lub jego krewnych) i metody 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("Błąd podczas odczytu pliku: " + e.getMessage());
        }
    }
}

Tutaj otwieramy plik do odczytu i za pomocą readLine() czytamy po jednym wierszu, aż dojdziemy do końca (null). Następnie wypisujemy każdy wiersz z numerem.

Krótko o try-with-resources

Widzisz konstrukcję try (...) { ... }? To try-with-resources. Gwarantuje, że plik zostanie zamknięty nawet wtedy, gdy w trakcie odczytu wystąpi błąd. Nie musisz ręcznie wywoływać close(): zamknięcie nastąpi automatycznie, nawet przy catch/finally.

Po co jest BufferedReader?

BufferedReader czyta nie pojedyncze znaki, lecz całe bloki (zwykle po 8192 bajtów), co przyspiesza pracę z plikami. Ponadto ma wygodną metodę readLine(), która zwraca wiersz aż do znaku końca linii.

Jaki bufor wybrać?

Zwykle BufferedReader używa bufora o rozmiarze 8192 bajtów (8 KB) — to wystarcza w większości zadań. Jeśli czytasz bardzo długie wiersze (np. po 100_000 znaków), bufor można zwiększyć:

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

Ale do zwykłych zadań standardowy rozmiar jest idealny.

2. Odczyt pliku w całości: Files.readAllLines i Files.readString

Jeśli plik jest niewielki (np. do 10–20 MB), wygodnie wczytać go w całości. Na przykład, gdy trzeba szybko uzyskać listę wszystkich wierszy, przeanalizować tekst lub wysłać go po sieci.

Nowoczesny sposób: Files.readAllLines

Od Javy 7 pojawiła się wygodna klasa Files z wieloma przydatnymi metodami.

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("Błąd podczas odczytu pliku: " + e.getMessage());
        }
    }
}

Tutaj metoda Files.readAllLines(path) zwraca listę wierszy (List<String>). Z taką listą można pracować jak ze zwykłą kolekcją: wyszukiwać, sortować, filtrować.

Jeszcze nowocześniejsze: Files.readString (Java 11+)

Jeśli potrzebujesz uzyskać cały plik jako jeden łańcuch (np. do wyszukania podciągu lub przekazania w JSON), użyj 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("Zawartość pliku:");
            System.out.println(content);
        } catch (IOException e) {
            System.out.println("Błąd podczas odczytu pliku: " + e.getMessage());
        }
    }
}

A co z kodowaniem?

Domyślnie metody Files.readAllLines i Files.readString używają standardowego kodowania Twojego systemu. Jeśli plik zapisano w innym kodowaniu (np. Windows-1251), wskaż je jawnie:

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

3. Porównanie podejść: kiedy które stosować

Sposób Kiedy używać Zalety Wady
BufferedReader.readLine()
Duże pliki, przetwarzanie wierszami Oszczędza pamięć, elastyczne Tylko odczyt wierszy
Files.readAllLines()
Małe i średnie pliki Od razu lista wierszy, proste Dla dużych plików — OutOfMemoryError
Files.readString()
Małe pliki, potrzebny cały tekst Cały tekst w jednym Stringu Brak podziału na wiersze

Rekomendacja:
— Jeśli plik jest niewielki — użyj Files.readAllLines lub Files.readString.
— Jeśli plik jest duży albo nie znasz jego rozmiaru — użyj BufferedReader.readLine().

4. Zadania praktyczne: przykłady i omówienie

Przykład 1. Zliczanie wierszy w dużym pliku

Załóżmy, że musisz sprawdzić, ile wierszy jest w ogromnym pliku (np. w logu serwera).

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("Łączna liczba wierszy w pliku: " + lineCount);
        } catch (IOException e) {
            System.out.println("Błąd podczas odczytu pliku: " + e.getMessage());
        }
    }
}

Przykład 2. Wyszukiwanie wierszy po treści

Szukamy wszystkich wierszy, w których pojawia się słowo „błąd” (bez rozróżniania wielkości liter):

import java.io.*;

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

        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("Błąd podczas odczytu pliku: " + e.getMessage());
        }
    }
}

Przykład 3. Wczytywanie konfiguracji z niewielkiego pliku

Plik config.txt:

host=localhost
port=8080
mode=dev

Wczytujemy go w całości i rozbijamy na pary klucz-wartość:

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; // pomijamy puste linie i komentarze
            String[] parts = line.split("=", 2);
            if (parts.length == 2) {
                config.put(parts[0].trim(), parts[1].trim());
            }
        }

        System.out.println("Wczytana konfiguracja: " + config);
    }
}

5. Typowe błędy przy odczycie plików tekstowych

Błąd nr 1: Próba odczytu pliku binarnego jako tekstowego. Jeśli otworzysz obraz lub archiwum przez BufferedReader albo Files.readAllLines, otrzymasz bezsensowne znaki i ryzyko OutOfMemoryError. Do plików binarnych używaj InputStream!

Błąd nr 2: Brak obsługi wyjątku. Pliki mogą zostać usunięte, przeniesione, zablokowane. Zawsze opakowuj odczyt w try-catch i informuj użytkownika o problemach.

Błąd nr 3: Ignorowanie kodowania. Jeśli masz tekst po rosyjsku, a czytasz plik bez wskazania kodowania, możesz zobaczyć „?????”. Używaj StandardCharsets.UTF_8 lub potrzebnego kodowania.

Błąd nr 4: Zapomniane zamknięcie strumienia. Jeśli nie zamkniesz pliku, może pozostać zablokowany do zakończenia programu. Zawsze używaj try-with-resources.

Błąd nr 5: Używanie read() zamiast readLine() dla plików tekstowych. Metoda read() czyta znak po znaku — to wolne i niewygodne dla wierszy. Do tekstu używaj readLine() albo metod klasy Files.

1
Zadanie
JAVA 25 SELF, poziom 36, lekcja 1
Niedostępne
Łączenie fragmentów wielkiej sagi 📖
Łączenie fragmentów wielkiej sagi 📖
1
Zadanie
JAVA 25 SELF, poziom 36, lekcja 1
Niedostępne
Wyszukiwanie wspólnych tajemnic w szpiegowskich teczkach 🕵️
Wyszukiwanie wspólnych tajemnic w szpiegowskich teczkach 🕵️
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION