Что вернет метод read если он считывает файл и ему встречается байт равный 1

Обновлено: 06.07.2024

Потому что byte может удерживать только -128 до 127, тогда как он должен возвращать 0 до 255 (и -1, когда нет байта слева (то есть EOF)). Даже если он вернется byte , не будет места для представления EOF.

Более интересный вопрос заключается в том, почему он не возвращает short .

@Ishtar Так что вы говорите, что шорты абсолютно бесполезны и никогда не будут использоваться никем? Я должен сказать, что все это очень расстраивает. 8 бит буквально одно число слишком короткое для этой цели. Разве они не могут вернуть его, а затем просто сгенерировать исключение на EOF? Я знаю, что общая философия Java говорит не использовать обработку исключений для потока управления, но есть исключения для всего. Если вы возвращаете byte (неявное приведение) в реализации InputStream , не забудьте применить & 0xFF к вашему возвращаемому значению (если только вы не EOF ). В противном случае вы вернете значение со знаком, которое может работать на первый взгляд, но на самом деле совершенно не работает. Я думаю, что short не используется, потому что он не настроен на машинное слово java, которое составляет 4 байта. int точно соответствует одному слову и работает со значениями int быстрее, потому что избегает корректировки памяти в результатах. По той же причине все классы в куче perm и instance тоже настроены на 4 байта. Почему это не относится к чтению (byte [] b)? Итак, данные считываются в byte массив, но почему не в массив int ?

Он возвращает int, потому что, когда поток больше не читается, он возвращает -1.

Если он возвращает байт, то -1 не может быть возвращен, чтобы указать ошибку, потому что -1 является допустимым байтом. Кроме того, вы не можете вернуть значение выше 127 или ниже -128, потому что Java обрабатывает только подписанные байты.

Много раз, когда вы читаете файл, вам нужны неподписанные байты для вашего кода обработки. Чтобы получить значения от 128 до 255, вы можете использовать короткий, но с помощью int вы будете более эффективно выравнивать регистры памяти с вашей шиной данных. В результате вы действительно не теряете какую-либо информацию, используя int, и вы, вероятно, получаете немного производительности. Единственным недостатком является стоимость памяти, но, скорее всего, вы не будете долго висеть на этом int (как вы ее обработаете и превратите в char или byte []).

Объясните, почему метод read () потока байтов Java возвращает int вместо byte

Все мы знаем, что операции 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 вместо байта, в основном, чтобы избежать преждевременного завершения операции потока и гарантировать, что данные могут быть полностью прочитаны.

Я изучаю java.io. В документации метода read () я видел определение:

Метод read () возвращает ASCII-код входных байтов (0-255) и возвращает -1 в конце файла

Более того, как я знаю, ASCII-код EOF - 26 .

Итак, почему метод read() возвращает -1 вместо 26 для EOF. И что означает возвращаемое значение -1 ?

Еще один вопрос: для чего используется нулевой символ (т. Е. NUL ), код ASCII: 0? И если файл пуст (то есть не имеет данных), существует ли символ NUL или нет?

2 ответа

Я видел определение: «метод read () возвращает ASCII-код входных байтов (0-255) и возвращает -1 в конце файла»

Это определение неверно. Метод read() не возвращает ASCII. Он возвращает байты и не помещает на них интерпретацию . Для двоичного файла они определенно не являются кодами ASCII.

Вот реальное определение InputStream.read() . как определено в javadoc:

« public abstract int read() throws IOException

Читает следующий байт данных из входного потока. Байт значения возвращается как целое число в диапазоне от 0 до 255. Если байт не доступен, поскольку достигнут конец потока, возвращается значение -1. "

Обратите внимание, что нет упоминания об ASCII.

Более того, как я знаю, ASCII-код EOF равен 26.

На самом деле, нет символа ASCII, который означает EOF. Код 26 (CTRL-Z) является символом ASCII SUB. Он используется при вводе с клавиатуры для обозначения EOF в Windows, но не в других контекстах. Действительно, в Mac OS и Linux код 4 ASCII (CTRL-D) служит этой цели.

В любом случае все значения байтов без знака от 0 до 255 являются действительными значениями данных, которые могут отображаться в файле. Там необходимо использовать другое значение для обозначения EOF.

Еще один вопрос: для чего используется нулевой символ (NUL), код ASCII: 0?

Все виды вещей. Действительно, все, что приложение выбирает для его использования.

А если файл пустой (данных нет), символ NUL существует или нет?

Символ NUL не представляет пустой файл или конец файла.

Если файл не имеет данных, его длина будет равна нулю. Длина файла является частью метаданных файла, так же как его имя файла, его владелец и группа, его разрешения, метка времени его создания и так далее.

Все эти вещи мы можем назвать одним словом — процесс обмена данными между программой и внешним миром. Хотя это уже не одно слово.

Сам процесс обмена данными можно разделить на два типа: получение данных и отправка данных. Например, вы считываете данные с клавиатуры с помощью объекта 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. Класс 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() .

Читайте также: