1. Чтение файла построчно: BufferedReader и компания
Ранее мы уже знакомились с побайтовым чтением: это удобно для работы с бинарными форматами, но для текста такой подход неудобен. Файл состоит из символов, которые зависят от кодировки. Поэтому Java предлагает удобные «надстройки» — FileReader, BufferedReader и другие классы, которые превращают поток байтов в поток символов и строк.
Представьте текстовый файл — будь то лог работы программы, список пользователей или даже огромный роман «Война и мир». Иногда нужно быстро прочитать весь файл целиком, иногда — пройтись по нему построчно, а иногда — вытащить одну конкретную строку.
В Java есть несколько способов это сделать, и выбор зависит от размера файла и задачи. Если нужно обработать файл по одной строке (например, подсчитать количество строк или найти запись), используют построчное чтение. А если файл небольшой — его можно загрузить в память целиком и работать с ним как со списком строк.
Почему построчно — это хорошо?
Для больших файлов построчное чтение спасает от проблем с памятью. Загружать в память гигабайтный лог — плохая идея: велик шанс нарваться на OutOfMemoryError. А вот считывать файл строка за строкой можно без особых затрат, даже если он весит сотни мегабайт.
Как это делается в Java?
Самый классический способ — использовать BufferedReader (или его родственников) и метод 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("Ошибка при чтении файла: " + e.getMessage());
}
}
}
Здесь мы открываем файл для чтения и с помощью readLine() читаем по одной строке, пока не дойдём до конца (null). После этого выводим каждую строку с номером.
Кратко о try-with-resources
Видите конструкцию try (...) { ... }? Это try-with-resources. Она гарантирует, что файл закроется, даже если в середине чтения случится ошибка. Вам не нужно вручную вызывать close(): закрытие произойдёт автоматически, даже при catch/finally.
Зачем нужен BufferedReader?
BufferedReader читает не по одному символу, а целыми блоками (обычно по 8192 байта), что ускоряет работу с файлами. Кроме того, у него есть удобный метод readLine(), который возвращает строку сразу до символа перевода строки.
Какой буфер выбрать?
Обычно BufferedReader использует буфер размером 8192 байта (8 КБ) — этого достаточно для большинства задач. Если вы читаете очень длинные строки (например, по 100_000 символов), буфер можно увеличить:
BufferedReader reader = new BufferedReader(new FileReader(fileName), 65536); // 64 КБ буфер
Но для обычных задач стандартный размер подходит идеально.
2. Чтение файла целиком: Files.readAllLines и Files.readString
Если файл небольшой (например, до 10–20 МБ), его удобно загрузить целиком. Например, если нужно быстро получить список всех строк, проанализировать текст или отправить его по сети.
Современный способ: Files.readAllLines
С Java 7 появился удобный класс Files с кучей полезных методов.
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("Ошибка при чтении файла: " + e.getMessage());
}
}
}
Здесь метод Files.readAllLines(path) возвращает список строк (List<String>). С этим списком можно работать как с обычной коллекцией: искать, сортировать, фильтровать.
Ещё современнее: Files.readString (Java 11+)
Если вам нужно получить весь файл одной строкой (например, для поиска подстроки или передачи в JSON), используйте 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("Содержимое файла:");
System.out.println(content);
} catch (IOException e) {
System.out.println("Ошибка при чтении файла: " + e.getMessage());
}
}
}
А что с кодировкой?
По умолчанию методы Files.readAllLines и Files.readString используют стандартную кодировку вашей системы. Если файл записан в другой кодировке (например, Windows-1251), укажите её явно:
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
String content = Files.readString(path, StandardCharsets.UTF_8);
3. Сравнение подходов: когда какой использовать
| Способ | Когда использовать | Преимущества | Недостатки |
|---|---|---|---|
|
Большие файлы, построчная обработка | Экономит память, гибко | Чтение только по строкам |
|
Маленькие и средние файлы | Сразу список строк, просто | Для больших файлов — OutOfMemory |
|
Маленькие файлы, нужен весь текст | Вся строка целиком | Нет разбивки на строки |
Рекомендация:
— Если файл небольшой — используйте Files.readAllLines или Files.readString.
— Если файл большой или вы не знаете его размер — используйте BufferedReader.readLine().
4. Практические задачи: примеры и разбор
Пример 1. Подсчёт строк в большом файле
Допустим, нужно узнать, сколько строк в огромном файле (например, логе сервера).
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("Всего строк в файле: " + lineCount);
} catch (IOException e) {
System.out.println("Ошибка при чтении файла: " + e.getMessage());
}
}
}
Пример 2. Поиск строки по содержимому
Ищем все строки, где встречается слово «ошибка» (case-insensitive):
import java.io.*;
public class FindErrorLines {
public static void main(String[] args) {
String fileName = "biglog.txt";
String keyword = "ошибка";
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("Ошибка при чтении файла: " + e.getMessage());
}
}
}
Пример 3. Загрузка конфигурации из небольшого файла
Файл config.txt:
host=localhost
port=8080
mode=dev
Читаем его целиком и разбираем на пары ключ-значение:
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; // пропускаем пустые и комментарии
String[] parts = line.split("=", 2);
if (parts.length == 2) {
config.put(parts[0].trim(), parts[1].trim());
}
}
System.out.println("Загруженная конфигурация: " + config);
}
}
5. Типичные ошибки при чтении текстовых файлов
Ошибка №1: Попытка читать бинарный файл как текстовый. Если вы откроете изображение или архив через BufferedReader или Files.readAllLines, получите бессмысленные символы и риск OutOfMemoryError. Для бинарных файлов используйте InputStream!
Ошибка №2: Не обработали исключение. Файлы могут удалить, переместить, заблокировать. Всегда оборачивайте чтение в try-catch и информируйте пользователя о проблемах.
Ошибка №3: Игнорирование кодировки. Если у вас текст на русском, а вы читаете файл без указания кодировки, можно получить «?????». Используйте StandardCharsets.UTF_8 или нужную вам кодировку.
Ошибка №4: Забыли закрыть поток. Если не закрыть файл, он может остаться заблокированным до завершения программы. Всегда используйте try-with-resources.
Ошибка №5: Использование read() вместо readLine() для текстовых файлов. Метод read() читает по символу — это медленно и неудобно для строк. Для текста используйте readLine() или методы класса Files.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ