Почему новая String (bytes, enc) .getBytes (enc) не возвращает исходный байтовый массив? - PullRequest
17 голосов
/ 30 марта 2010

Я сделал следующую «симуляцию»:

byte[] b = new byte[256];

for (int i = 0; i < 256; i ++) {
    b[i] = (byte) (i - 128);
}
byte[] transformed = new String(b, "cp1251").getBytes("cp1251");

for (int i = 0; i < b.length; i ++) {
    if (b[i] != transformed[i]) {
        System.out.println("Wrong : " + i);
    }
}

Для cp1251 выводится только один неправильный байт - в позиции 25.
Для KOI8-R - все хорошо.
Для cp1252 - 4 или 5 отличий.

В чем причина этого и как это можно преодолеть?

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

Обновление: представление его в ISO-8859-1 работает, и я буду использовать его для части byte[] и cp1251 для текстовой части, поэтому вопрос остается только из любопытства 1022 *

Ответы [ 5 ]

11 голосов
/ 30 марта 2010

Некоторые из «байтов» не поддерживаются в целевом наборе - они заменяются символом ?. При обратном преобразовании ? обычно преобразуется в байтовое значение 63 - это не то, что было раньше.

7 голосов
/ 30 марта 2010

В чем причина этого

Причина в том, что кодировки символов не обязательно биективны , и нет веских оснований ожидать, что они будут. Не все байты или последовательности байтов являются допустимыми во всех кодировках, и, как правило, недопустимые последовательности декодируются в какой-то символ-заполнитель, такой как '?' или U + FFFD , что, конечно, не выдает те же байты при перекодировании.

Кроме того, некоторые кодировки могут отображать некоторые допустимые различные последовательности байтов в одну и ту же строку.

4 голосов
/ 30 марта 2010

Похоже, что и cp1251, и cp1252 имеют байтовые значения, которые не соответствуют определенным символам; то есть они "не отображаемы".

Javadoc для String(byte[], String) говорит это:

Поведение этого конструктора, когда указанные байты недопустимы в данной кодировке, не определено. Класс CharsetDecoder следует использовать, когда требуется больший контроль над процессом декодирования.

Другие конструкторы говорят это:

Этот метод всегда заменяет последовательности некорректного ввода и непопределяемого символа на строку замены этого набора символов по умолчанию.

Если вы видите, что такого рода вещи случаются на практике, это указывает на то, что либо вы используете неправильный набор символов, либо вам были переданы некоторые неверные данные. В любом случае, это, вероятно, не самая лучшая идея, как если бы не было никаких проблем.

Я пытался выяснить, есть ли способ заставить CharsetDecoder «сохранить» не отображаемые символы, и я не думаю, что это возможно, если вы не хотите реализовать пользовательскую пару декодер / кодировщик. Но я также пришел к выводу, что нет смысла даже пытаться. (Теоретически) неправильно отображать эти не отображаемые символы в реальные кодовые точки Unicode. И если да, то как ваше приложение справится с ними?

3 голосов
/ 30 марта 2010

На самом деле должно быть одно различие: байт значения 24 преобразуется в char значения 0xFFFD; это «символ замены Unicode», используемый для непереводимых байтов. При обратном преобразовании вы получите знак вопроса (значение 63).

В CP1251 код 24 означает «конец ввода» и не может быть частью правильной строки, поэтому Java считает ее «непереводимой».

2 голосов
/ 30 марта 2010

Историческая причина: в древних кодировках символов (EBCDIC, ASCII) первые 32 кода имеют специальное значение «control» и могут не отображаться на читаемые символы. Примеры: возврат, звонок, возврат каретки. Более новые стандарты кодирования символов обычно наследуют это, и они не определяют символы Unicode для каждой из первых 32 позиций. Java-символы являются Unicode.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...