Страницы

Поиск по вопросам

понедельник, 19 ноября 2018 г.

Как хранят информацию потоки ввода-вывода?

Объясните, пожалуйста, почему при вводе в консоли, скажем, буквы ‘t’ и последующих выводах System.in.read() и new BufferedReader(new InputStreamReader(System.in)).read(), получается один и тот же результат при приведении к типу int, а именно: 116. Ведь стандартный read() байтового потока должен читать по одному байту, и выдать ноль (как первый байт двухбайтной записи ‘t’), а символьный BufferedReader всё выдаёт правильно. В чём дело?


Ответ

Консоль передает готовые байты.
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 и пропустить его.

Комментариев нет:

Отправить комментарий