Почему длины отличаются при преобразовании байтового массива в строку и затем обратно в байтовый массив? - PullRequest
4 голосов
/ 16 февраля 2011

У меня есть следующий код Java:

byte[] signatureBytes = getSignature();

String signatureString = new String(signatureBytes, "UTF8");
byte[] signatureStringBytes = signatureString.getBytes("UTF8");

System.out.println(signatureBytes.length == signatureStringBytes.length); // prints false

Q: Возможно, я неправильно понимаю это, но я подумал, что new String(byte[] bytes, String charset) и String.getBytes(charset) - обратные операции?

Q: В качестве продолжения, какой безопасный способ передачи массива byte [] в виде строки?

Ответы [ 3 ]

8 голосов
/ 16 февраля 2011

Не каждый byte[] является действительным UTF-8.По умолчанию недопустимые последовательности заменяются фиксированным символом, и я думаю, что причина такого изменения длины.

Попробуйте Latin-1, этого не должно быть, так как это простая кодировка, для которой каждый byte[]имеет смысл.

Ни для Windows-1252 это не должно произойти.Там есть неопределенные последовательности (фактически неопределенные байты), но все символы кодируются одним байтом.Новый byte[] может отличаться от исходного, но его длина должна быть одинаковой.

5 голосов
/ 16 февраля 2011

Возможно, я неправильно понимаю это, но я подумал, что новые String (byte [] bytes, String charset) и String.getBytes (charset) являются обратными операциями?

Не обязательно.

Если входной байтовый массив содержит последовательности, которые не являются допустимыми UTF-8, то первоначальное преобразование может превратить их (например) в вопросительные знаки.Затем вторая операция превращает их в '?' символы в кодировке UTF-8 ... в отличие от исходного представления.


Это правда, что некоторые символы в Unicode имеют несколько представлений;например, акцентированные символы могут быть одной кодовой точкой или базовой кодовой точкой и акцентной точкой.Однако преобразование туда и обратно между байтовым массивом (содержащим действительный UTF-8) и String должно сохранить последовательности кодовых точек.Он не выполняет какую-либо «нормализацию».


Так какой же безопасный способ передачи массива byte [] в виде String тогда?

самой безопасной альтернативой было бы base64-кодирование байтового массива.Это имеет дополнительное преимущество, заключающееся в том, что символы в строке будут преобразованы в любой набор символов / кодировку, который может представлять латинские буквы и цифры.

Другой альтернативой является использование Latin-1 вместо UTF-8.Однако:

  • Существует риск повреждения, если данные будут (например) ошибочно интерпретированы как UTF-8.
  • Этот подход недопустим, если «строка» тогдавстроенный в XML.Многие управляющие символы находятся за пределами набора символов XML и не могут использоваться в документе XML, даже закодированы в виде символов.
2 голосов
/ 16 февраля 2011

На ум приходят две возможности.

Во-первых, ваша подпись не совсем действительна в формате UTF8. Вы не можете просто взять произвольные двоичные данные и «натянуть» их. Не каждый кусочек бит определяет юридический характер. Конструктор String вставит некоторый замещающий контент по умолчанию для двоичных данных, который на самом деле ничего не «значит» в UTF8. Это не обратимый процесс. Если вы хотите «String» некоторых произвольных двоичных данных, для этого вам нужно использовать установленный метод, я бы предложил org.apache.commons.codec.binary.Base64

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

...