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 |
|---|---|---|---|
|
Duże pliki, przetwarzanie wierszami | Oszczędza pamięć, elastyczne | Tylko odczyt wierszy |
|
Małe i średnie pliki | Od razu lista wierszy, proste | Dla dużych plików — OutOfMemoryError |
|
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.
GO TO FULL VERSION