#java #кодировка #поток_ввода
Объясните, пожалуйста, почему при вводе в консоли, скажем, буквы ‘t’ и последующих выводах System.in.read() и new BufferedReader(new InputStreamReader(System.in)).read(), получается один и тот же результат при приведении к типу int, а именно: 116. Ведь стандартный read() байтового потока должен читать по одному байту, и выдать ноль (как первый байт двухбайтной записи ‘t’), а символьный BufferedReader всё выдаёт правильно. В чём дело?
Ответы
Ответ 1
Консоль передает готовые байты. System.in — это входной поток процесса Java, который управляется ОС. Консоль передает в этот поток уже преобразованные байты. Что происходит когда Вы вводите данные в консоли: Консоль преобразовывает введенные символы в байты в соотвествии с кодировкой заданной для консоли. Установка кодировки зависит от отдельно взятой консоли/ОС. Скорее всего в Вашей консоли установлена кодировка, которая преобразовывает t в один байт. System.in.read честно считывает полученный байт. InputStreamReader преобразовывает полученные байты в символы в соответствии с кодировкой по-умолчанию, т.к. Вы выбрали конструктор с незаданой кодировкой. Кодировка по-умолчанию задается параметрами JVM. Можете проверить кодировку Вашего reader-а следующим кодом: InputStreamReader reader = new InputStreamReader(System.in); System.out.println("Encoding: "+reader.getEncoding()); Кодировку можно задать явно, воспользовавшись одним из конструкторов. При считывании reader считывает байты из входного потока и преобразовывает их в соответствии с кодировкой в числовое значение соответствующего символа (char). Согласно спецификации Java (§3.1 Unicode), символы хранятся в кодировке UTF-16. Допустим мы используем однобайтную кодировку, вводим символ «ы» и обрабатываем его: char value = (char) reader.read(); Reader прочитает из входного потока один байт, поймет что это за символ найдет этот символ в таблиц UTF-16 и вернет его числовое значение. В резултате этот код будет эквивалентен: char value = 'ы'; независимо от кодировки. Двухбайтные кодировки Чтобы увидеть двухбайтное кодирование стандартного ввода нужно установить соответствующую кодировку. В целях экономии места символы английского алфавита в большинстве кодировок кодируются одним байтом. ... должен читать по одному байту, и выдать ноль (как первый бит двухбитной записи ‘t’) Так символы преобразовываются в двухбайтных кодировках с порядком от старшего к младшему (например, UTF16-BE). Соответственно, чтобы увидеть такое преобразование через System.in.read Вам нужно подавать данные на вход в такой кодировке. Это можно сделать рядом способов, например: Прочитать документацию консоли, узнать как задать для нее кодировку и поддерживается ли кодировка UTF16-BE. Если да, то задать кодировку. Если нет, то этот способ не подойдет. Не вводить данные с консоли, а передать на вход файл с текстом. Файл сохранить в кодировке UTF16-BE, после чего выполнить команду вроде: java Main < encoded.txt Нужно принять во внимание, что при сохранении первым символом в файле будет BOM и пропустить его.Ответ 2
При явном приведении строки к массиву байтов: import java.util.Arrays; public class App { public static void main(String[] args) { String str = "t"; byte[] b = str.getBytes(); System.out.println(b.length); //длина массива System.out.println(Arrays.toString(b)); //значение } } увидим в консоли 1 [116] Длина массива из строки "t" это 1, а искомый байт 116. Дейстивительно, как выше ответил @default locale, read() честно отдает этот байт. P.S. В случае с двубайтным символом, например "ы", метод getBytes() дает массив из двух байт [-47, -117], а если считывать этот символ из потока методом System.in.read(), мы получим тоже два байта 209 и 139. Почему так, потому что (цитирую из "Программирование на Java", Патрик Нимейер, Дэниэл Леук, 4-е издание 2014 г., стр.568 ) на платформе Java для выделения конца потока данный метод использует специальное значение, следуя стандарту из языка С. Байты возвращаются в виде беззнаковых целых чисел в диапазоне от 0 до 255; Закономерность смещения значения байтов простая - явное приведение int к byte. (byte) 209 и (byte) 139 соответственно -47 и -117. В случае, если мы читаем методом read() из потока типа BufferedReader, то возвращаем кодировку символа, как сказано в документации: The character read, as an integer in the range 0 to 65535 (0x00-0xffff)
Комментариев нет:
Отправить комментарий