BufferedReader: примеры (JAVA)
BufferedReader(Reader in)Описание класса BufferedReader и его назначение
Класс BufferedReader из пакета java.io обеспечивает буферизованное чтение символов, массивов и строк из источника типа Reader. Основная цель класса - уменьшить количество обращений к медленным источникам (файлы, сетевые потоки) за счет чтения больших блоков данных в память и предоставления методов для удобного построчного чтения.
Типичные сценарии использования: чтение текстовых файлов, обработка стандартного ввода, чтение сетевых ответов в виде текста, предварительная буферизация для парсинга.
Конструкторы
BufferedReader(Reader in)- создает буфер по умолчанию (обычно 8192 символа) вокруг переданногоReader.BufferedReader(Reader in, int sz)- создает буфер указанного размераsz. Еслиsz<= 0, будет выброшеноIllegalArgumentException.
Основные методы и их поведение
int read()- читает один символ и возвращает его код в видеint; при достижении конца источника возвращает-1. Может выброситьIOException.int read(char[] cbuf, int off, int len)- читает доlenсимволов в массивcbuf, начиная с индексаoff. Возвращает число фактически прочитанных символов или-1при EOF.String readLine()- возвращает следующую строку без символов конца строки; если достигнут EOF и байтов/символов нет, возвращаетnull. Не включает символы '\n' или '\r'.Stream- возвращает ленивый поток строк (Java 8+); поток должен быть закрыт вместе с читателем или в try-with-resources.lines() boolean ready()- сообщает, готов ли поток для немедленного чтения (не блокирует). Возвращаетfalseпри EOF или если источник не готов.long skip(long n)- пропускает доnсимволов, возвращает число фактически пропущенных символов.void mark(int readAheadLimit)- помечает текущую позицию в потоке с возможностью чтения доreadAheadLimitсимволов и возврата черезreset(). Поддержка пометки реализована; параметр определяет, сколько символов можно прочесть до того, как отметка станет недействительной.void reset()- возвращает позицию на последнее место, где была вызванаmark(). Если отметка отсутствует или потеряна, выбрасываетIOException.boolean markSupported()- возвращаетtrue(BufferedReader поддерживает mark/reset).void close()- закрывает поток и освобождает ресурсы; при закрытии источника автоматически закрывается вложенныйReader.
Возвращаемые значения и исключения
Методы чтения возвращают количество прочитанных символов или специальные значения (-1 для EOF, null для readLine). Большинство методов могут выбросить IOException при ошибках ввода-вывода. При некорректных аргументах конструктора или методов возможны IllegalArgumentException и IndexOutOfBoundsException для параметров массива.
Советы по использованию:
- Для автоматического закрытия использовать try-with-resources (Java 7+).
- При работе с файлами учитывать кодировку: применять
InputStreamReaderилиFiles.newBufferedReader(Path, Charset)для явного указания charset. - Подбирать размер буфера под ожидаемую нагрузку: большие файлы - больший буфер может дать прирост производительности, но увеличит использование памяти.
Короткие примеры использования BufferedReader
Четыре коротких примера: чтение файла построчно, чтение из стандартного ввода, чтение символов и использование lines().
1. Чтение файла построчно
import java.io.*;
public class ReadFile {
public static void main(String[] args) throws Exception {
try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
}
}
(если example.txt содержит:) Hello World 123
2. Чтение из System.in (одна строка)
import java.io.*;
public class StdinExample {
public static void main(String[] args) throws Exception {
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
String s = br.readLine();
System.out.println("You typed: " + s);
}
}
}
(ввод: "test") You typed: test
3. Чтение в массив символов
try (BufferedReader br = new BufferedReader(new StringReader("abcd"))) {
char[] buf = new char[3];
int n = br.read(buf, 0, buf.length);
System.out.println(n + " -> " + new String(buf, 0, n));
}
3 -> abc
4. Использование streams - lines()
try (BufferedReader br = new BufferedReader(new StringReader("a\nb\nc"))) {
br.lines().map(String::toUpperCase).forEach(System.out::println);
}
A B C
Аналоги в Java и их особенности
- Scanner - удобен для токенизации и разбора чисел/слов; медленнее для простого побайтового чтения и построчного чтения больших файлов. Предпочтителен при необходимости парсинга по разделителям.
- FileReader / InputStreamReader без буфера - обеспечивает чтение, но при каждом вызове read может обращаться к источнику. BufferedReader поверх них повышает производительность.
- Files.newBufferedReader(Path, Charset) (NIO) - удобный способ получить BufferedReader с явной кодировкой; рекомендуется для современных приложений, работающих с файлами и кодировками.
- BufferedInputStream - для двоичных данных; не предоставляет построчных методов. Предпочтение зависит от того, текстовые или бинарные данные нужны.
Выбор зависит от задачи: при чтении и построчной обработке большого текста - BufferedReader; при синтаксическом парсинге токенов со вспомогательными методами - Scanner; при работе с байтами - BufferedInputStream.
Аналоги в других языках и отличия
Ниже краткие примеры для разных языков и их особенности по сравнению с Java BufferedReader.
PHP
$f = fopen('example.txt', 'r');
while (($line = fgets($f)) !== false) {
echo $line;
}
fclose($f);
Hello World 123
fgets читает строку; buffering управляется внутренне; при необходимости использовать stream_set_blocking или large buffers.
JavaScript (Node.js)
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: fs.createReadStream('example.txt'),
});
rl.on('line', line => console.log(line));
Hello World 123
Readline предоставляет построчное чтение для потоков; буферизация происходит в Readable stream.
Python
with open('example.txt', 'r', encoding='utf-8') as f:
for line in f:
print(line.rstrip())
Hello World 123
Встроенный итератор файла уже буферизован; можно использовать io.BufferedReader для работы с байтовыми потоками.
C#
using (var sr = new System.IO.StreamReader("example.txt"))
{
string line;
while ((line = sr.ReadLine()) != null)
Console.WriteLine(line);
}
Hello World 123
StreamReader похож по назначению на BufferedReader; внутренне применяет буферизацию и поддерживает кодировки.
Go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
f, _ := os.Open("example.txt")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
Hello World 123
bufio.Reader и bufio.Scanner выполняют буферизацию и построчное чтение. Scanner имеет ограничения по длине строки без доп. настроек.
Kotlin
import java.io.File
fun main() {
File("example.txt").useLines { lines ->
lines.forEach { println(it) }
}
}
Hello World 123
Kotlin использует Java-API; BufferedReader доступен как file.bufferedReader() и имеет удобные расширения.
Lua
local f = io.open('example.txt', 'r')
for line in f:lines() do
print(line)
end
f:close()
Hello World 123
Lua предоставляет простое построчное чтение; буферизация управляется реализацией I/O в среде.
SQL
Прямого аналога нет: SQL работает с табличными данными; чтение файлов обычно выполняется средствами СУБД (LOAD DATA, BULK INSERT) или внешним кодом, который использует соответствующие API для буферизации.
Типичные ошибки и примеры
- Забыть закрыть поток - приводит к утечке дескрипторов. Пример: отсутствие try-with-resources приводит к открытому файлу до завершения программы.
- Неправильная кодировка - использование
FileReaderпо умолчанию может привести к кракозябрам при несовпадении кодировок. - Некорректное использование mark/reset -
reset()может выброситьIOException, если было прочитано больше символов, чем задано вreadAheadLimit. - Обращение к read после close() - вызовы после закрытия приведут к
IOExceptionили неопределенному поведению.
Пример 1: EOF и read()
BufferedReader br = new BufferedReader(new StringReader(""));
int c = br.read();
System.out.println(c); // -1 при EOF
-1
Пример 2: Кодировка при использовании FileReader
// example.txt в кодировке UTF-8, но FileReader использует платформенную кодировку
try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
System.out.println(br.readLine());
}
(может быть неправильно декодированная строка при несовпадении кодировок)
Пример 3: mark/reset превышение readAheadLimit
try (BufferedReader br = new BufferedReader(new StringReader("abcdefghijklmnopqrstuvwxyz"))) {
br.mark(5);
char[] buf = new char[6];
br.read(buf, 0, 6); // прочитает 6 символов
br.reset(); // IOException: отметка недействительна
}
IOException: Stream reset() without valid mark
Рекомендация: использовать try-with-resources, указывать Charset при чтении файлов и внимательно управлять mark/reset.
Изменения в поведении BufferedReader в новых версиях
Класс BufferedReader стабилен и существенных изменений в API не претерпевал. Основные моменты:
- Java 7: появление try-with-resources облегчило безопасное закрытие (класс реализует AutoCloseable).
- Java 8: добавлен метод
lines(), возвращающий Stream строк, что облегчает интеграцию с Stream API. - В последних релизах производительность платформы и реализаций I/O могла улучшаться, но публичное API осталось прежним.
Расширенные и редкие варианты использования
Несколько расширенных примеров: кастомный размер буфера, повторное чтение фрагмента с помощью mark/reset, обработка больших файлов параллельно (через stream API с осторожностью), чтение с явной кодировкой через NIO.
1. Кастомный размер буфера для больших файлов
try (BufferedReader br = new BufferedReader(new FileReader("big.txt"), 65536)) {
String line;
while ((line = br.readLine()) != null) {
// обработка
}
}
(нет конкретного текстового вывода, пример показывает настройку буфера 64К)
2. Повторное чтение участка с mark/reset
String data = "header\npayload line1\npayload line2\n";
try (BufferedReader br = new BufferedReader(new StringReader(data))) {
// считывается заголовок
String header = br.readLine();
br.mark(100); // пометка перед полезной частью
String first = br.readLine();
System.out.println("First: " + first);
br.reset(); // возвращаемся и читаем сначала полезную часть
System.out.println("After reset: " + br.readLine());
}
First: payload line1 After reset: payload line1
3. Использование Files.newBufferedReader с явной кодировкой
import java.nio.file.*;
import java.nio.charset.*;
try (BufferedReader br = Files.newBufferedReader(Paths.get("example_utf8.txt"), StandardCharsets.UTF_8)) {
br.lines().forEach(System.out::println);
}
(вывод содержимого в корректной кодировке UTF-8)
4. Парсинг CSV с предварительной буферизацией и peek
// Простой peek-подход: прочитать первую строку, проверить заголовок, затем обрабатывать
try (BufferedReader br = new BufferedReader(new FileReader("data.csv"))) {
br.mark(1024);
String header = br.readLine();
if (header != null && header.contains("id,name")) {
// пропустить заголовок - уже прочитан
} else {
br.reset(); // нет заголовка - вернуться
}
br.lines().forEach(line -> {
String[] parts = line.split(",");
// обработать parts
});
}
(зависит от содержимого data.csv)
5. Чтение очень больших файлов в кусках и параллельная обработка партий
import java.nio.file.*;
// пример концепции: читать блоки строк и отправлять их в executor
// (не рекомендуется разбивать single BufferedReader между потоками без синхронизации)
(пример архитектуры, результат зависит от реализации)
Пояснения: mark/reset полезны для небольших «просмотров» данных; для больших lookahead лучше использовать буферизованные очереди. При параллельной обработке рекомендуется разбивать файл по позициям с помощью NIO (FileChannel) вместо совместного использования одного BufferedReader.