Проблема с кодировкой Java 8 UTF-8 (ошибка java?)

При создании строки с кодировкой UTF-8 существует несогласованность.

Запустите этот код:

public static void encodingIssue() throws IOException {
    byte[] array = new byte[3];
    array[0] = (byte) -19;
    array[1] = (byte) -69;
    array[2] = (byte) -100;

    String str = new String(array, "UTF-8");
    for (char c : str.toCharArray()) {
        System.out.println((int) c);
    }
}

В Java 1.8.0_20 (и более ранних версиях) мы имеем результат

 65533

В Java 1.7 и 1.6 мы получаем правильный результат:

 57052

Вы столкнулись с этой ошибкой? Есть ли обходной путь для этого?

Эта несогласованность проявляется также для Shift_JIS, JIS_X0212-1990, x-IBM300, x-IBM834, x-IBM942, x-IBM942C, x-JIS0208, но, очевидно, UTF-8 является более актуальным.

Ответы

Ответ 1

Это свойство кодировки "Модифицированный UTF-8" для хранения суррогатных пар (или даже непарных символов этого диапазона), таких как отдельные символы. И его ошибка, если декодер, претендующий на использование стандартного UTF-8, использует "Модифицированный UTF-8". Кажется, что это исправлено с Java 8.

Вы можете надежно читать такие данные, используя метод, который указан для использования "Модифицированный UTF-8":

ByteBuffer bb=ByteBuffer.allocate(array.length+2);
bb.putShort((short)array.length).put(array);
ByteArrayInputStream bis=new ByteArrayInputStream(bb.array());
DataInputStream dis=new DataInputStream(bis);
String str=dis.readUTF();

Ответ 2

Значение, полученное в Java 1.6/1.7 - это U + DEDC (низкий суррогат).

Из RFC 3629:

Определение UTF-8 запрещает кодирование номеров символов между U + D800 и U + DFFF, которые зарезервированы для использования с формой кодирования UTF-16 (в качестве суррогатных пар) и не представляют непосредственно символы.

... текст пропущен...

Реализации вышеописанного алгоритма декодирования ДОЛЖНЫ защищать от декодирования недопустимых последовательностей. Например, наивная реализация может декодировать слишком длинную последовательность C0 80 UTF-8 в символ U + 0000 или суррогатную пару ED A1 8C ED BE B4 в U + 233B4. Декодирование недопустимых последовательностей может иметь последствия для безопасности или вызывать другие проблемы.

Java 8 декодирует это в U + FFFD (CHARACTER ЗАМЕНЫ). Это похоже на ошибку, которая была исправлена в Java 8.

Ответ 3

Это суррогат, верно? Я не специалист Юникод, но я не думаю, что он имеет смысл сам по себе. Java 8 изменена для поддержки Unicode 6.2. Может быть, это более строго. 65533 является стандартным символом замены 0xFFFD, что означает, что "не представимо". Есть ли реальный случай, когда вам нужно интерпретировать это как строку? потому что кажется, что Unicode говорит, что это больше не имеет смысла как персонаж.