В java почему метод read () из FileInputStream работает, а не «несовместимые типы: возможное преобразование с потерями»? - PullRequest
0 голосов
/ 02 марта 2020

В настоящее время я изучаю Java I / O учебник и не могу понять метод read () класса FileInputStream. Я знаю, что для каждого документа метод read () читает «байт» данных из потока и возвращает целое число, представляющее байт (от 0 до 256) или -1, если он достигает конца файла.

Байт в java имеет диапазон от -128 до 127, поэтому, когда я отредактирую xanadu.txt и добавлю символ ASCI "ƒ" (который имеет десятичное значение 131), java не жалуется, выдавая ошибку, что значение 131 находится вне диапазона, определенного байтом (-128 и 127)? Когда я пытаюсь проверить это с помощью литералов, я получаю два разных результата.

Следующее работает:

byte b = 120;
int c = b;
System.out.println((char)c);

Output: x

Но это НЕ работает (даже если оно работает при добавлении в xanadu.txt) :

byte b = 131;
int c = b;
System.out.println((char)c);

Output: error: incompatible types: possible lossy conversion from int to byte
        byte b = 131;

Я попытался явным образом привести с использованием байта: (как это возможно?)

byte b = (byte)131;
int c = b;
System.out.println((char)c);

Output: テ

Я новичок ie, когда дело доходит до потоков ввода-вывода, кто-то пожалуйста, помогите мне понять это.

РЕДАКТИРОВАТЬ: Оказывается, мне не хватало моих знаний о понятиях приведения типов, особенно в понимании различий между «расширением» и «сужением». Ознакомление с этими понятиями помогло мне понять, почему работает явное (иначе говоря, сужение) приведение типов.

Позвольте мне объяснить: посмотрите на 3-й блок кода, где я явно преобразую литерал «131» в тип байта. Если мы хотим преобразовать литерал 131 в двоичную форму 32-разрядного целого числа со знаком 2 со знаком, мы получим 00000000 00000000 00000000 10000011, что составляет 32 бита или 4 байта. Напомним, что Java тип данных 'byte' может содержать только 8-разрядное целое число со знаком 2 со знаком, поэтому 131 находится вне диапазона, и, таким образом, мы получаем ошибку "возможное преобразование с потерями из int в байты". Но когда мы явно приводим его к байту, мы «обрезаем», или правильный термин будет «сужать» двоичное число до 8-битного целого числа. Таким образом, когда мы делаем это, то получаемый двоичный файл равен 10000011, который равен -125 в десятичном значении. Поскольку -125 находится в диапазоне от -128 до 127, у байта нет проблем с его принятием и хранением. Теперь, когда я пытаюсь представить значение байта в int c, происходит неявное или «расширяющее» приведение, где -125 в двоичной форме 8-битного 10000011 преобразуется в эквивалентный -125 в двоичной форме 32-битного 11111111 11111111 11111111 10000011. Наконец, system.out пытается вывести значение (char) c, которое является другим явным или «сужающимся» приведением, когда оно пытается сжаться с 32-разрядной подписи до 16-разрядной подписи без знака. Когда приведение завершено, мы получаем 11111111 10000011 в двоичном виде. Теперь, когда этот двоичный файл преобразован в символьную форму с помощью java, он возвращает テ.

В заключение я могу сказать, что это помогает преобразовать все в двоичную форму и go оттуда. Но убедитесь, что вы понимаете кодировку и дополнение 2

Ответы [ 2 ]

0 голосов
/ 02 марта 2020

Я не знаю, откуда вы взяли значение 131, но, насколько мне известно, LATIN SMALL LETTER F WITH HOOK (ƒ) не в исходном наборе символов ASCII, а в расширенном ASCII с десятичным знаком значение 159. См. здесь . Он также кодируется в UTF-16 (как кодируются Java char s) как шестнадцатеричный 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-битного, а затем обработку его как целого числа без знака.

Причина, по которой все это работает, когда вы используете FileInputStream.read вместо того, чтобы делать эти преобразования самостоятельно, потому что read на самом деле возвращает int, а не byte. Просто int, который он возвращает, всегда будет в диапазоне -1 ~ 255. Вот почему мы говорим "read возвращает байт", но его реальный тип возврата - int.

.
0 голосов
/ 02 марта 2020
byte b = 131;  // this is 8 bits type, but >8 bits value
int c = b;     // this is 32 bits type
System.out.println((char)c);  // this is 16 bits type

Output: error: incompatible types: possible lossy conversion from int to byte
        byte b = 131;

Двухкомпонентная кодировка 131:

2^7+2^1+2^0
^^^
sign bit

131 не помещается в знаковый байт без переполнения в двухкомплементном представлении , который используется для подписанных типов. Устанавливается старший бит = знаковый бит, который расширяется при преобразовании из байта в int.

Компилятор Java замечает, что 131 не помещается должным образом в байт, что приводит к ошибке.

...