Почему чтение символа, не имеющего представления ASCII, с помощью System.in не дает символа в два байта? - PullRequest
1 голос
/ 26 мая 2020
import java.io.IOException;

public class Main {

    public static void main(String[] args) throws IOException {

        char ch = '诶';

        System.out.println((int)ch);

        int c;
        while ((c = System.in.read()) != -1)
        {
            System.out.println(c);
        }
    }
}

Вывод:

35830

Здесь значение, представляющее char в юникоде, - 35830. В двоичном формате это будет 10001011 11110110.

Когда я ввожу этот символ в терминал, я ожидал получить два байта, 10001011 и 11110110. и при их повторном объединении я могу получить исходный символ.

Но на самом деле я получаю:

232
175
182
10

Я вижу, что 10 представляет собой символ новой строки. Но что означают первые 3 числа?

1 Ответ

5 голосов
/ 26 мая 2020

UTF-8 - это многобайтовая кодировка переменной длины.

Чтобы что-то, читающее поток байтов, знало, что есть еще байты, которые нужно прочитать, чтобы завершить sh текущий codepoint, есть некоторые значения, которые просто не могут встретиться в допустимом потоке байтов UTF-8. В основном, определенные шаблоны указывают на «подожди, я еще не закончил».

Есть таблица, которая объясняет это здесь . Для кодовой точки в диапазоне от U + 0800 до U + FFFF для ее представления требуется 16 бит; его байтовое представление состоит из 3 байтов:

1st byte    2nd byte    3rd byte
1110xxxx    10xxxxxx    10xxxxxx

Вы видите 232 175 182, потому что это байты кодировки UTF-8.

byte[] bytes = "诶".getBytes(StandardCharsets.UTF_8);
for (byte b : bytes) {
  System.out.println((0xFF & b) + " " + Integer.toString(0xFF & b, 2));
}

Демо Ideone

Вывод:

232 11101000
175 10101111
182 10110110

Итак, 3 байта следуют шаблону, описанному выше.

...