Что вернет метод read если он считывает файл и ему встречается байт равный 1
Все эти вещи мы можем назвать одним словом — процесс обмена данными между программой и внешним миром. Хотя это уже не одно слово.
Сам процесс обмена данными можно разделить на два типа: получение данных и отправка данных. Например, вы считываете данные с клавиатуры с помощью объекта Scanner — это получение данных. И выводите данные на экран с помощью команды System.out.println() — это отправка данных.
Для описания процесса обмена данными в программировании используется термин поток. Откуда вообще взялось такое название?
В реальной жизни им может быть поток воды или поток людей (людской поток). В программировании же под потоком подразумевают поток данных.
Потоки — это универсальный инструмент. Они позволяют программе получать данные откуда угодно (входящие потоки) и отправляют данные куда угодно (исходящие потоки). Делятся на два вида:
Чтобы потоки можно было «потрогать руками», разработчики Java написали два класса: InputStream и OutputStream .
У класса InputStream есть метод read() , который позволяет читать из него данные. А у класса OutputStream есть метод write() , который позволяет записывать в него данные. У них есть и другие методы, но об этом после.
Байтовые потоки
Что же это за данные и в каком виде их можно читать? Другими словами, какие типы данных поддерживаются этими классами?
О, это универсальные классы, и поэтому они поддерживают самый распространённый тип данных — byte . В OutputStream можно записывать байты (и массивы байт), а из объекта InputStream можно читать байты (или массивы байт). Все — никакие другие типы данных они не поддерживают.
Поэтому такие потоки еще называют байтовыми потоками .
Особенность потоков в том, что данные из них можно читать (писать) только последовательно. Вы не можете прочитать данные из середины потока, не прочитав все данные перед ними.
Именно так работает чтение с клавиатуры через класс Scanner : вы читаете данные с клавиатуры последовательно: строка за строкой. Прочитали строку, прочитали следующую строку, прочитали следующую строку и т.д. Поэтому метод чтения строки и называется nextLine() (дословно — «следующая срока»).
Запись данных в поток OutputStream тоже происходит последовательно. Хороший пример — вывод на экран. Вы выводите строку, за ней еще одну и еще одну. Это последовательный вывод. Вы не можете вывести 1-ю строку, затем 10-ю, а затем вторую. Все данные записываются в поток вывода только последовательно.
Символьные потоки
Недавно вы изучали, что строки — второй по популярности тип данных, и это действительно так. Очень много информации передается в виде символов и целых строк. Компьютер отлично бы передавал все в виде байт, но люди не настолько идеальны.
Java-программисты учли этот факт и написали еще два класса: Reader и Writer . Класс Reader — это аналог класса InputStream , только его метод read() читает не байты, а символы — char . Класс Writer соответствует классу OutputStream , и так же, как и класс Reader , работает с символами ( char ), а не байтами.
Если сравнить эти четыре класса, мы получим такую картину:
Байты (byte) | Символы (char) |
---|---|
Чтение данных | |
Запись данных |
Практическое применение
Сами классы InputStream , OutputStream , Reader и Writer в явном виде никто не использует: они не присоединены ни к каким внешним объектам, из которых можно читать данные (или в которые можно писать данные). Однако у этих четырех классов много классов-наследников, которые умеют очень многое.
2 ответа 2
- .available() не блокирует поток выполнения при отсутствии данных , а .read() - блокирует.
- .available() возвращает примерное количество доступных для чтения байтов, а .read(byte[] b) - количество считанных в буфер (массив байтов), от -1 при достижении конца потока данных и до размера массива ( b.length ).
- .available() лишь проверяет наличие данных, а .read() - их считывает.
Как работает input.read(new byte[input.read()]) :
- Происходит считывание одного байта из потока с помощью input.read() .
- Его значение (в данном случае код символа j - 106 ), которое возвращает .read() (именно значение первого байта, а не количество байтов), используется для создания массива из 106 байтов.
- В данный байтовый массив считываются данные из потока.
- Данных в потоке оставалось 8 байтов (первый уже был считан в пункте 1), и в буфер они входят ( 106 > 8 ), так что .read(byte[] b) возвращает количество считанных байтов - 8.
Возможно я не верно понимаю, но в файле у меня 9 символов, соответственно ожидаемый результат - 9 байт. Метод .available() возвращает ожидаемый результат. Что именно я не учитываю в своих рассуждениях @regent ?
И да, правильно ли я понимаю, что конструкция byte[] b = new byte[input.available()] - заведомо некорректна, для будущего использования метода .read()?
@mamba0767 да, заведомо некорректна в том плане что это не даст гарантий, что вы прочитаете за раз все доступные байты. По поводу вашего примера из вопроса - обновил ответ, добавив объяснение того, что там происходит.
@mamba0767 для простейшего байтового чтения из файла я бы использовал int read = input.read(buffer); внутри while (read > 0) . Это, по-моему, лучше, чем читать по одному байту. Можно и всякие обёртки над InputStream использовать для, например, более удобного, построчного чтения из текстового файла.
Все мы знаем, что операции io в java делятся на байтовые потоки и символьные потоки. Для байтовых потоков, как следует из названия, читаются данные в байтах, поэтому мы часто используем байтовые потоки для чтения двоичных потоков (таких как изображения, музыка и другие файлы) ). Возникает вопрос, почему метод read (), определенный в потоке байтов, возвращает тип int? Поскольку он читает по одному байту данных за раз, почему он не возвращает байтовый тип?
Нельзя сказать, что онлайн-инструкция ошибочна, но я не думаю, что она четко объяснена. Затем давайте возьмем в качестве примеров FileInputStream / FileOutputStream и BufferedInputStream / BufferedOutputStream. Эти два объяснят, почему это так и каков промежуточный процесс реализации.
1. С точки зрения FileInputStream / FileOutputStream, это в основном связано с вызываемым собственным методом. Я хочу знать, как реализован собственный уровень.
Исходный код read () в FileInputStream:
Здесь мы не можем понять, почему метод read () возвращает тип int, поэтому давайте углубимся и посмотрим на код слоя Native:
Здесь метод Java_java_io_FileInputStream_read0 — это собственный метод, вызываемый read0 () в JDK.
Как вы можете видеть здесь, наша цель — вернуть тип int -1, если чтение закончено, иначе будет возвращено ret (здесь байтовые данные). Возврат будет байтовым, а & 0xFF должен гарантировать, что тип байта При расширении вверх до int не выполняется раскрытие знака, а расширение 0.
Выше приведен процесс реализации. Я считаю, что все здесь знают, почему read () потока ввода-вывода Java возвращает int вместо byte, потому что нижний уровень расширяет byte int.
Итак, почему вы это делаете? Мы знаем, что байтовые входные потоки могут работать с файлами любого типа, такими как изображения, аудио и т. Д. Эти файлы хранятся в двоичной форме на нижнем уровне. Если байт возвращается каждый раз при чтении, он может быть прочитан 111111111, а 11111111 — это байтовый тип -1, программа прекратит чтение, когда встретит -1. При получении 11111111 с типом int она добавит 24 0, чтобы составить 4 байта, затем байтовый тип -1 становится 255 типа int, что гарантирует чтение всех данных. -1 конечного тега имеет тип int для оценки, как показано ниже.
(Здесь мы всегда должны понимать истину, конечный флаг -1 возвращается, судя о конце файла, а не входной поток читает -1, чтобы избежать Эта ситуация!)
Использование & 0xFF является причиной 0 расширения (данные, хранящиеся в компьютере, имеют форму дополнения, если вы не понимаете, пожалуйста, сначала поймите)
1 байт 8 бит, (байт) 4 байта 32 бита, (int)
byte -1 -> int -1 (преобразовать байт в int)
байт составляет 1 байт, то есть 8 бит. Если вы получите 11111111 последовательно, чтобы избежать чтения 8 последовательных единиц (то есть -1), это то же самое, что и определенный конечный тег -1 (чтение () Верните -1, чтобы прочитать до конца). Следовательно, на основе сохранения 11111111 при преобразовании в тип int первые 24 бита заполняются 0 вместо 1.
Если это 1 11111111 11111111 11111111 11111111, не так ли -1?
Таким образом, первые 24 бита заполняются 0, -1 становится 255, что позволяет сохранить исходные байтовые данные неизменными (самые младшие 8 бит, взаимодействовать с методом записи для принудительного преобразования) и избежать -1. Появляются.
Мы продолжаем рассматривать метод записи в FileOutputStream, который также вызывает собственный метод.
Посмотрите прямо на код слоя Native:
Здесь нужно записать байты по одному.Перед записью старшие 24-битные байты int были обработаны битовыми операциями в методе OutputStream.write (int).
JDK четко заявил
Таким образом обеспечивается безошибочное чтение и запись данных. Это также причина того, почему тип входного параметра метода write (int b) JDK OutputStream и его подклассов — int. (В Java есть байтовый тип, но нет в языке C, что может быть причиной использования int)
2. С точки зрения BufferedInputStream / BufferedOutputStream основной причиной является переписывание набора методов ввода-вывода.
Реализация метода read () в BufferedInputStream
Из исходного кода выше мы видим, что byte [], возвращаемый методом getBufIfOpen (), также 0 расширен на & 0xff, то есть последняя строка метода read () расширяет прочитанный байт 0 до int. Реализуйте возврат одного байта данных, если он достигнет конца потока, он вернет -1 (вот тип int)
Метод writer () в BufferedOutputStream решительно превращает int в байт (восемь бит после перехвата)
Я должен это видеть, и я должен знать причину этого: при чтении байта данных с входным потоком иногда бывает 8 последовательных 1. Это значение представляет -1 внутри компьютера, что совпадает с концом метки потока. Поэтому, чтобы избежать преждевременного завершения данных потоковой операции, считанные байты расширяются в тип int. Сохраняя данные этого байта, добавьте 0 впереди, чтобы избежать ситуации -1.
На самом деле прочтите конец файла через это предложение: if (pos> = count) return -1;
Используемый нами конечный флаг -1 возвращается этим предложением вместо чтения -1 во входном потоке.
Наконец, чтобы подвести итог, метод read () в потоках ввода-вывода Java возвращает int вместо байта, в основном, чтобы избежать преждевременного завершения операции потока и гарантировать, что данные могут быть полностью прочитаны.
Потому что byte может удерживать только -128 до 127, тогда как он должен возвращать 0 до 255 (и -1, когда нет байта слева (то есть EOF)). Даже если он вернется byte , не будет места для представления EOF.
Более интересный вопрос заключается в том, почему он не возвращает short .
Он возвращает int, потому что, когда поток больше не читается, он возвращает -1.
Если он возвращает байт, то -1 не может быть возвращен, чтобы указать ошибку, потому что -1 является допустимым байтом. Кроме того, вы не можете вернуть значение выше 127 или ниже -128, потому что Java обрабатывает только подписанные байты.
Много раз, когда вы читаете файл, вам нужны неподписанные байты для вашего кода обработки. Чтобы получить значения от 128 до 255, вы можете использовать короткий, но с помощью int вы будете более эффективно выравнивать регистры памяти с вашей шиной данных. В результате вы действительно не теряете какую-либо информацию, используя int, и вы, вероятно, получаете немного производительности. Единственным недостатком является стоимость памяти, но, скорее всего, вы не будете долго висеть на этом int (как вы ее обработаете и превратите в char или byte []).
Что происходит с числом byte == -1 в методе read()
То же самое: убираем лидирующие единицы.
Спасибо, классные ответы, но я несовсем разобрался. что будет если read прочтет -1 из потока? Он сделает побитовое И с 255, потом обратно ИЛИ с -256? Что в итоге? Получается int-овое представление будет совершенно другим? Как получить значение -1?
Если в методе read встречается byte со значением -1 то он вернет 255? В чем секрет? Как с этим работать?)
На все 4 вопроса вообще один ответ. Возвращается int потому что надо такой тип, который может вместить в себя один байт (реальные данные) плюс одно служебное значение (это тот самый -1 ), которое является признаком окончания чтения.
Вообще конечно можно бы было спроектировать метод read() так, чтобы он возвращал byte . Но тогда этот метод в случае окончания потока либо должен был бросать исключение, либо вводить допольнительный метод, при помощи которого бы можно было проверять окончание потока. С обоими способами бы были проблемы, т.к. с исключениями у программистов всегда бы оно ловилось, а природа исключений немного другая. А с дополнительным методом нельзя заставить обязать программиста его везде вызывать (ведь если этот вызов будет опущен, то как отделить, когда из потока методом read возращается 0 с данными от 0 когда данных нет.
Вообще говоря, этот метод редко используется, в основном из-за своих проблем с производительностью. Предпочтение отдаётся методу read(byte[]), который читает сразу массив байт.
P.S. Другой вопрос почему тут выбрали int , а не short . Точных причин сказать не могу, но скорее потому, что short считается неполноценным братом int . Многие арифметические алгоритмы предпочитают использовать всегда int , даже когда точно известно, что диапазона short вполне хватит. И ещё есть момент с short . JVM-инструкции более заточены на int , нежели на short .
Считывая с файла потоком FileInputStream input получаем данные значения. Разве метод available не должен возвращать количество доступных битов так же как и вторая конструкция?
файл содержит следующий текст:
Что происходит с числом byte в методе read
Чтобы получить представление byte в int в методе read() используется побитовое «И» c числом 255, т. е. убираем лидирующие единицы.
Чтобы из представления получить обратно значение byte в int, нужно выполнить обратную операцию побитовое «ИЛИ» c числом -256, т. е. добавляем лидирующие единицы.
Почему в java метод read () из FileInputStream работает не выдает «несовместимые типы: возможное преобразование с потерями»?
В настоящее время я прохожу учебник по вводу-выводу Java и получаю трудно понять метод read () класса FileInputStream. Я знаю, что для каждого документа метод read () читает «байт» данных из потока и возвращает целое число, представляющее байт (от 0 до 256) или -1, если он достигает конца файла.
Байт в java имеет диапазон от -128 до 127, поэтому, когда я редактирую xanadu.txt и добавляю символ ASCI «ƒ» (который имеет десятичное значение 131), java не жалуется, выдавая ошибку, значение 131 находится вне диапазона, определяемого байтом (-128 и 127)? Когда я пытаюсь проверить это с помощью литералов, я получаю два разных результата.
Но это НЕ работает (даже если оно работает при добавлении в xanadu.txt):
Я попытался явное приведение, используя байт:
Я полностью новичок, когда дело доходит до потоков ввода / вывода, кто-нибудь, пожалуйста, помогите мне понять это
Как хранятся значения в int в byte
Диапазон byte в Java лежит от -128 до 127, а возвращаемое значение метода read() лежит в диапазоне от 0 до 255.
2. Класс InputStream
Класс InputStream интересен тем, что является классом-родителем для сотен классов-наследников. В нем самом нет никаких данных, однако у него есть методы, которые есть у всех его классов-наследников.
Объекты-потоки вообще редко хранят в себе данные. Поток — это инструмент чтения/записи данных, но не хранения. Хотя бывают и исключения.
Методы класса InputStream и всех его классов-наследников:
Методы | Описание |
---|---|
Читает один байт из потока | |
Читает массив байт из потока | |
Читает все байты из потока | |
Пропускает n байт в потоке (читает и выкидывает) | |
Проверяет, сколько байт еще осталось в потоке | |
Закрывает поток |
Вкратце пройдемся по этим методам:
Метод read()
Метод read() читает один байт из потока и возвращает его. Вас может сбить тип результата — int , однако так было сделано, потому что тип int — это стандарт всех целых чисел. Три первые байта типа int будут равны нулю.
Метод read(byte[] buffer)
Это вторая модификация метода read() . Он позволяет считать из InputStream сразу массив байт. Массив для сохранения байт нужно передать в качестве параметра. Метод возвращает число — количество реально прочитанных байт.
Допустим у вас буфер на 10 килобайт, и вы читаете данные из файла с помощью класса FileInputStream . Если файл содержит всего 2 килобайта, все данные будут помещены в массив-буфер, а метод вернет число 2048 (2 килобайта).
Метод readAllBytes()
Очень хороший метод. Просто считывает все данные из InputStream , пока они не закончатся, и возвращает их в виде единого массива байт. Очень удобен для чтения небольших файлов. Большие файлы могут физически не поместиться в память, и метод кинет исключение.
Метод skip(long n)
Этот метод позволяет пропустить n первых байт из объекта InputStream . Поскольку данные читаются строго последовательно, этот метод просто вычитывает n первых байт из потока и выбрасывает их.
Возвращает число байт, которые были реально пропущены (если поток закончился раньше, чем прокрутили n байт).
Метод int available()
Метод возвращает количество байт, которое еще осталось в потоке
Метод void close()
Метод close() закрывает поток данных и освобождает связанные с ним внешние ресурсы. После закрытия потока данные из него читать больше нельзя.
Давайте напишем пример программы, которая копирует очень большой файл. Его нельзя весь считать в память с помощью метода readAllBytes() . Пример:
InputStream для чтения из файла
OutputStream для записи в файл
Буфер, в который будем считывать данные
Пока данные есть в потоке
В этом примере мы использовали два класса: FileInputStream — наследник InputStream для чтения данных из файла, и класс FileOutputStream — наследник OutputStream для записи данных в файл. О втором классе расскажем немного позднее.
Еще один интересный момент — это переменная real . Когда из файла будет читаться последний блок данных, легко может оказаться, что его длина меньше 64Кб. Поэтому в output нужно тоже записать не весь буфер, а только его часть: первые real байт. Именно это и делается в методе write() .
В консоль выводится 253, а не -3.
Почему так происходит?
Тип данных int в Java является дополненнным до двух целым числом и использует 32 бита вместо 8.
В 32х битном виде число 253 будет:
т. е. метод read() возвращает не само значение byte, а его представление в 32х битном виде.
2 ответа
Кодирование 131 с двумя дополнениями:
131 не поместится в подписанный байт без переполнения в двух дополняющее представление, которое используется для подписанных типов. Устанавливается старший бит = знаковый бит, который расширяется при приведении от байта к внутр.
Компилятор Java замечает, что 131 не помещается должным образом в байт, что приводит к предупреждению.
Я не знаю, откуда вы взяли значение 131, но, насколько мне известно, LATIN SMALL LETTER F WITH HOOK (ƒ) находится не в исходном наборе символов ASCII, а в расширенном ASCII с десятичным значением 159. См. здесь. Он также кодируется в UTF-16 (как кодируются Java char ) как шестнадцатеричное 192 (десятичное значение 402).
Во-первых, убедитесь, что ваши текстовые файлы закодированы в расширенном ASCII, а не в UTF-8 (что является наиболее вероятной кодировкой). Затем вы можете использовать FileInputStream для read файла, и вы получите 159 .
Обратите внимание, что 159 находится вне диапазона типа Java byte . Это нормально, потому что read возвращает int . Если текстовый файл закодирован в UTF-8, однако, ƒ закодирован в 2 байта, поэтому read будет читать по одному байту за раз.
Ваш второй блок кода не работает, потому что, как вы сказали, byte изменяется от -128 до 127, поэтому 131, очевидно, не подходит.
Ваш третий кодовый блок вынуждает 131 в байте, что вызывает переполнение, и значение «оборачивается» до -125. b и c оба -125. Когда вы приводите это к char , оно становится 65411, потому что это преобразование включает сначала заполнение целого числа до 16-бит, а затем его обработку как целого числа без знака.
— Привет, Амиго! Сегодня мы снова будем заниматься разбором работы InputStream и OutputStream. На самом деле, то первое объяснение было немного упрощенным. Это не интерфейсы, а абстрактные классы, и они даже имеют по паре реализованных методов. Давай посмотрим, какие методы у них есть:
Методы класса InputStream | Что метод делает |
---|---|
— метод сразу читает блок байт в буфер ( массив байт ), пока буфер не заполнится или не закончатся байты там, откуда он их читает. Метод возвращает количество реально прочитанных байт (оно может быть меньше длины массива) | |
— метод читает один байт и возвращает его как результат. Результат расширяется до int, для красоты. Если все байты уже прочитаны, метод вернет «-1». | |
— метод возвращает количество непрочитанных (доступных) байт. | |
— метод «закрывает» поток – вызывается после окончания работы с потоком. Объект выполняет служебные операции, связанные с закрытием файла на диске и т.д. Из потока больше нельзя читать данные. |
— Т.е. мы можем читать не только по одному байту, а и целыми блоками?
— А записывать целыми блоками тоже можно?
Методы OutputStream | Что метод делает |
---|---|
— метод записывает один байт информации. Тип int сужается до byte, лишняя часть просто отбрасывается. | |
— метод записывает блок байт. | |
— метод записывает часть блока байт. Используется в случаях, когда есть вероятность, что блок данных был заполнен не целиком | |
— если есть данные, которые хранятся где-то внутри и еще не записаны, то они записываются. | |
— метод «закрывает» поток – вызывается после окончания работы с потоком. Объект выполняет служебные операции, связанные с закрытием файла на диске и т.д.В поток больше нельзя писать данные, flush при этом вызывается автоматически. |
— А как будет выглядеть код копирования файла, если мы будем читать не по одному байту, а целыми блоками?
— Гм. Примерно так:
— С буфером все понятно, а что это за переменная count?
— Когда мы читаем самый последний блок данных в файле, может оказаться, что байт осталось не 1000, а, скажем, 328. Тогда и при записи нужно указать, что записать не весь блок, а только его первые 328 байт.
Метод read при чтении последнего блока вернет значение равное количеству реально прочитанных байт. Для всех чтений – 1000, а для последнего блока – 328.
Поэтому при записи блока мы указываем, что нужно записать не все байты из буфера, а только 328 (т.е. значение, хранимое в переменной count).
Читайте также: