java.io.InputStream
). В последнее время такие проблемы почему-то стали появляться особенно часто, в связи с чем я решил разъяснить принцип раз и навсегда и просто давать всем желающим ссылку.Чаще всего чтение производят с помощью метода
int read()
. Этот вариант, безусловно, имеет право на существование, если знать, как дальше быть с возвращаемым результатом (который int
, а вовсе не byte
). Мне лично ближе чтение блоками. Оно как-то понятнее и избавляет от всех вопросов преобразования значения.Итак, код:
// входной поток, получается откуда-то извне InputStream is; // буфер для чтения, разумного объема byte[] buffer = new byte[32768]; // Выходной поток, ByteArrayOutputStream используется только для примера ByteArrayOutputStream baos = new ByteArrayOutputStream(); // цикл чтения while (true){ // читаем данные в буфер int readBytesCount = is.read(buffer); if (readBytesCount == -1) { // данные закончились break; } if (readBytesCount > 0) { // данные были считаны - есть, что записать baos.write(buffer, 0, readBytesCount); } } baos.flush(); baos.close(); byte[] data = baos.toByteArray();Разбираем, что к чему.
На входе у нас есть поток. Безразлично, откуда он взялся. Мы из него читаем. Что делается дальше:
- Выделяется буфер разумного размера. В этом примере - 32Кб, в принципе, размер зависит только от объема читаемых данных (если надо прочитать 100 байт, выделять 1Мб будет неразумно)
- Дальше в примере создается
ByteArrayOutputStream
. Это просто пример выходного потока, если у вас есть, куда писать данные - этот шаг необязателен. :) - Дальше начинается цикл чтения. Он бесконечный, выход реализован внутри. В цикле мы делаем следующее:
- Читаем данные в буфер с помощью метода
is.read(byte[] buffer)
. В этом методе будет прочитано от 0 доbuffer.length
байт. Это надо запомнить на всю жизнь - никто не гарантирует вычитывания всего буфера, даже если данных достаточно! Реальное количество прочитанных байтов возвращается из метода как значение. - Если реальное количество прочитанных байтов равно -1, это означает, что данные закончились. На это можно полагаться. В этом случае мы прерываем выполнение цикла чтения.
- Если прочитано больше нуля байтов (а может быть и ноль!) - мы записываем данные с помощью метода класса
OutputStream write(byte[] buffer, int position, int length)
. Обратите внимание - поскольку буфер может быть заполнен при последнем чтении не полностью, необходимо указывать, сколько байтов из него надо взять. - Продолжаем выполнение цикла.
- Читаем данные в буфер с помощью метода
- Сбрасываем остаток данных в поток и закрываем его
- Получаем данные в виде массива байтов
Еще я видел вариант, основанный на методе класса
InputStream - int available()
. Он возвращает количество байтов, доступных для чтения без блокировки. Это в теории. На практике - он реализован не во всех потоках, а реализация по умолчанию возвращает 0. Потому лично я его никогда не использую.Описанный метод является универсальным при чтении блоками. В частности, он работает при чтении из символьных потоков (
java.io.Reader
), вместо типа byte
при этом используется char
.Это всё. Всем спасибо!
С уважением,
Евгений aka Skipy
P.S. Комментарии? Дополнения?
P.P.S. Напоследок хочу напомнить. Читать из потока
byte
, преобразовывать его в char
простым приведением типа и пытаться построить из полученных "символов" строку - грубая ошибка. Это будет работать для латинских символов (да и то не во всех случаях!), а для нелатинских может работать лишь по счастливой случайности. Правильный вариант - сделать на основе InputStream
экземпляр Reader
, с указанием кодировки (new InputStreamReader(inputStream, "<имя кодировки>")
), и читать уже из него. О кодировках можно прочитать тут: http://www.skipy.ru/technics/encodings.html
Comments
А что делать, если нужно дважды записать что-то в один сокет?
byte[] transf = new byte[600];
Arrays.fill(transf, (byte)50);
out.write(transf);
out.flush();
out.write(transf);
out.flush();
на входе получается ситуация:
b 1200
c 0
или это уже проблема архитектуры? И нужно организовать вопрос-ответ?