InputStream.read: примеры (JAVA)
InputStream.read: intОбщее описание InputStream.read
Метод InputStream.read в Java используется для чтения байтов из потока ввода. Он представлен в нескольких перегруженных вариантах: int read(), int read(byte[] b) и int read(byte[] b, int off, int len). Поведение у всех вариантов общее: при достижении конца потока возвращается -1, при успешном чтении возвращается число фактически прочитанных байтов или байт (в случае read() возвращается значение байта в виде 0..255).
Подробно по вариантам:
- int read(): читает один байт и возвращает его как целое 0..255. Если поток закончился, возвращает
-1. Может блокировать выполнение до появления байта или конца потока. ВыбрасываетIOExceptionпри ошибке ввода-вывода. - int read(byte[] b): пытается заполнить весь массив
b. Возвращает количество прочитанных байтов (меньше длины массива возможно при достижении EOF или если чтение вернуло меньше данных). При EOF сразу возвращает-1. Может блокировать. ВыбрасываетIOException. - int read(byte[] b, int off, int len): читает до
lenбайтов и кладет их в массивb, начиная с индексаoff. Возвращает число прочитанных байтов или-1при EOF. Требует проверки границ аргументов:bне должен бытьnull,offиlenнеотрицательны,off + len <= b.length. На неверные аргументы выбрасываетсяIndexOutOfBoundsExceptionилиNullPointerException. Также может выброситьIOException.
Дополнительно, начиная с Java 9, в InputStream появились вспомогательные методы: readAllBytes(), readNBytes(int), readNBytes(byte[] b, int off, int len) и transferTo(OutputStream), упрощающие чтение всего содержимого или фиксированного числа байтов.
Особенности поведения:
- Методы могут блокировать, если источник данных блокирующий (например, сокет или System.in).
- Количества байтов, возвращаемые вариантом с массивом, могут быть меньше запрошенных, даже если не достигнут EOF. Нужно обрабатывать частичные чтения в цикле при необходимости получить точно N байтов.
- Метод
available()не гарантирует общее количество оставшихся байтов, он лишь оценивает количество немедленно доступных байтов.
Простые примеры использования
Ниже приведены короткие примеры основных вариантов чтения с показом кода и результата.
Чтение одного байта из ByteArrayInputStream
import java.io.*;
byte[] data = {65, 66};
try (InputStream in = new ByteArrayInputStream(data)) {
int b1 = in.read();
int b2 = in.read();
int b3 = in.read();
System.out.println(b1);
System.out.println(b2);
System.out.println(b3);
}
65 66 -1
Чтение в буфер
import java.io.*;
byte[] data = "Hello".getBytes();
try (InputStream in = new ByteArrayInputStream(data)) {
byte[] buf = new byte[3];
int n1 = in.read(buf); // может быть 3
int n2 = in.read(buf); // оставшиеся 2
System.out.println(n1);
System.out.println(n2);
System.out.println(new String(buf, 0, n2));
}
3 2 lo
Чтение с указанием offset и len
import java.io.*;
byte[] data = {1,2,3,4,5};
try (InputStream in = new ByteArrayInputStream(data)) {
byte[] buf = new byte[5];
int read = in.read(buf, 1, 3); // записать в buf[1..3]
System.out.println(read);
for (int i = 0; i < buf.length; i++) System.out.print(buf[i] + " ");
}
3 0 1 2 3 0
Похожие методы внутри Java
Внутри Java есть несколько альтернатив и надстроек над InputStream.read с отличиями по удобству и производительности:
- BufferedInputStream: оборачивает источник и уменьшает число системных вызовов, предпочтительнее при множественных мелких чтениях.
- DataInputStream: предоставляет методы для чтения примитивных типов (
readInt,readUTF), удобен при чтении бинарных форматов. - Files.readAllBytes(Path): читает весь файл в память, удобно для небольших файлов, не подходит для очень больших данных.
- ReadableByteChannel / FileChannel: NIO API, даёт неблокирующие и позиционные операции, предпочтение при высокопроизводительном вводе-выводе и работе с большими файлами.
- Reader (например InputStreamReader): для чтения текстовых данных с учётом кодировки; использовать вместо байтовых методов при работе с символами.
Выбор зависит от задачи: для побайтного чтения использовать BufferedInputStream, для парсинга бинарных форматов DataInputStream, для чтения всего контента Files.readAllBytes, для производительных и неблокирующих решений NIO-каналы.
Аналоги в других языках и отличия
Короткие примеры и отличия от Java.
- Python (io, open):
f.read(n)возвращает bytes или строку. Естьreadinto(buffer)для чтения в существующий буфер. Отличие: Python возвращает пустую bytes при EOF, Java возвращает -1. Пример:
# Python
with open('file.bin','rb') as f:
b = f.read(3)
print(b)
b2 = f.read(3)
print(b2)
b'Hel' b'lo'
- Node.js (JavaScript): поток fs.createReadStream, чтение через события 'data' или метод read на stream. Отличие: событийная модель и буферы Buffer. Пример:
// Node.js
const fs = require('fs');
const rs = fs.createReadStream('file.txt', { highWaterMark: 3 });
rs.on('data', chunk => console.log(chunk.toString()));
Hel lo
- PHP:
fopen/freadилиfile_get_contents. fread возвращает строку или false при ошибке. Пример:
<?
$h = fopen('file.txt','rb');
echo fread($h, 3);
echo fread($h, 3);
?>
Hel lo
- C# (.NET):
Stream.Read(byte[] buffer, int offset, int count)возвращает количество прочитанных байтов или 0 при EOF. Отличие: EOF возвращает 0, а не -1. Пример:
using(var fs = File.OpenRead("file.txt")){
byte[] buf = new byte[3];
int n = fs.Read(buf,0,3);
Console.WriteLine(n);
}
3
- Go: интерфейс io.Reader с методом
Read(p []byte) (n int, err error), при EOF возвращает n=0 и err=io.EOF. Отличие: явный err для EOF. Пример:
// Go
r := bytes.NewReader([]byte("Hello"))
b := make([]byte,3)
n, err := r.Read(b)
fmt.Println(n, string(b[:n]), err)
3 Hel <nil>
- Kotlin: использует Java InputStream, добавлены extension функции
readBytes()иreadNBytes(). Синтаксис и поведение аналогичны Java. - Lua:
io.read(n)читает n символов или nil при ошибке/EOF. - SQL: прямых аналогов нет, работу с бинарными данными выполняют драйверы DB (BLOB API), чтение через потоки драйвера.
Главное отличие большинства языков в представлении EOF: Java использует возвращаемое -1 для байтовых чтений и отдельные вспомогательные методы появились только в поздних версиях.
Типичные ошибки при использовании
Наиболее распространенные ошибки и их проявления.
- Непроверка значения возврата на
-1. Результат: использование -1 как байта, некорректные данные.
InputStream in = new ByteArrayInputStream(new byte[]{1});
byte[] b = new byte[2];
int n = in.read(b);
System.out.println(b[1]); // неинициализированное значение, если n==1
0 (возможна некорректная логика)
- Ожидание, что метод с массивом всегда заполнит весь массив. Результат: частичное чтение и неверная обработка данных. Решение: читать в цикле до получения нужного объема или до EOF.
byte[] buf = new byte[1024];
int read = in.read(buf);
// если требуется полностью 1024 байта, необходимо повторить чтение в цикле
- Неправильное использование
available()для определения EOF.available()может вернуть 0, хотя данных ещё есть; полагаться на него для чтения не рекомендуется. - Не закрытие потоков, особенно при работе с файлами и сетевыми ресурсами. Приводит к утечкам дескрипторов. Использование try-with-resources решает проблему.
- Неправильная обработка кодировок при преобразовании байтов в строки. Следует использовать явную кодировку:
new String(bytes, StandardCharsets.UTF_8). - Чтение больших файлов в память с помощью
readAllBytes()или накопление в ByteArrayOutputStream без лимитов ведет к OutOfMemoryError.
Изменения и дополнения в последних версиях Java
В Java 9 и выше были добавлены удобные методы в класс InputStream:
- readAllBytes(): читает весь поток в новый массив байтов. Удобно для небольших источников, потенциально опасно для больших данных.
- readNBytes(int) и readNBytes(byte[] b, int off, int len): читают заданное число байтов или до EOF и не блокируют сверх требуемого объема.
- transferTo(OutputStream): копирует оставшиеся данные в указанный OutputStream, оптимизировано для внутренних реализаций и часто быстрее ручного цикла с буфером.
Эти методы упростили распространённые сценарии чтения, уменьшили необходимость в собственных циклах и позволили задействовать оптимизированные реализации платформы.
Расширенные и редкие сценарии использования
Несколько подробных примеров с пояснениями и результатами.
Чтение точно N байтов с циклом (без Java 9)
import java.io.*;
byte[] source = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes();
try (InputStream in = new ByteArrayInputStream(source)) {
int toRead = 10;
byte[] out = new byte[toRead];
int read = 0;
while (read < toRead) {
int r = in.read(out, read, toRead - read);
if (r == -1) break; // EOF
read += r;
}
System.out.println(read);
System.out.println(new String(out, 0, read));
}
10 ABCDEFGHIJ
Использование readAllBytes() и предостережение
import java.io.*;
try (InputStream in = new FileInputStream("bigfile.bin")) {
byte[] all = in.readAllBytes();
System.out.println(all.length);
} catch (OutOfMemoryError e) {
System.out.println("Файл слишком большой");
}
(выведет размер массива или сообщение о нехватке памяти)
Копирование потока в другой поток с transferTo
import java.io.*;
byte[] data = "Hello transfer".getBytes();
try (InputStream in = new ByteArrayInputStream(data);
OutputStream out = new ByteArrayOutputStream()) {
in.transferTo(out);
System.out.println(out.toString());
}
Hello transfer
Чтение из сокета с таймаутом и обработка прерываний
import java.io.*;
import java.net.*;
Socket s = new Socket();
s.connect(new InetSocketAddress("example.com", 80), 2000);
s.setSoTimeout(3000);
try (InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream()) {
out.write("GET / HTTP/1.0\r\n\r\n".getBytes());
byte[] buf = new byte[4096];
int n;
while ((n = in.read(buf)) != -1) {
System.out.print(new String(buf, 0, n));
}
} catch (SocketTimeoutException e) {
System.out.println("Таймаут чтения");
}
(вывод HTTP-ответа или сообщение о таймауте)
Параллельное чтение с ограничением памяти: чтение чанками и сохранение в файл
import java.io.*;
try (InputStream in = new FileInputStream("large.bin");
OutputStream out = new FileOutputStream("copy.bin")) {
byte[] buf = new byte[64*1024];
int n;
while ((n = in.read(buf)) != -1) {
out.write(buf, 0, n);
}
}
(копирование файла по чанкам, экономия памяти)
Чтение в заранее выделенный ByteBuffer через InputStream -> Channel
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
try (ReadableByteChannel rbc = Channels.newChannel(new FileInputStream("file.bin"))) {
ByteBuffer bb = ByteBuffer.allocate(1024);
while (rbc.read(bb) != -1) {
bb.flip();
// обработка данных
bb.clear();
}
}
(побайтовая обработка с NIO)