BufferedReader: примеры (JAVA)

Чтение и парсинг текстовых данных
Раздел: Ввод-вывод (I/O) с буферизацией
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 lines() - возвращает ленивый поток строк (Java 8+); поток должен быть закрыт вместе с читателем или в try-with-resources.
  • 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. Кастомный размер буфера для больших файлов

Пример java
try (BufferedReader br = new BufferedReader(new FileReader("big.txt"), 65536)) {
    String line;
    while ((line = br.readLine()) != null) {
        // обработка
    }
}
(нет конкретного текстового вывода, пример показывает настройку буфера 64К)

2. Повторное чтение участка с mark/reset

Пример java
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 с явной кодировкой

Пример java
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

Пример java
// Простой 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. Чтение очень больших файлов в кусках и параллельная обработка партий

Пример java
import java.nio.file.*;

// пример концепции: читать блоки строк и отправлять их в executor
// (не рекомендуется разбивать single BufferedReader между потоками без синхронизации)
(пример архитектуры, результат зависит от реализации)

Пояснения: mark/reset полезны для небольших «просмотров» данных; для больших lookahead лучше использовать буферизованные очереди. При параллельной обработке рекомендуется разбивать файл по позициям с помощью NIO (FileChannel) вместо совместного использования одного BufferedReader.

джава BufferedReader function comments

En
BufferedReader Reads text from a character-input stream, buffering characters