Почему не `Encoding.UTF8.GetBytes (Encoding.UTF8.GetString (x)) == x` - PullRequest
5 голосов
/ 16 марта 2012

В .NET почему не так:

Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(x))

возвращает исходный байтовый массив для произвольного байтового массива x?

Это упомянуто в ответ на другой вопрос, но ответчик не объясняет почему.

Ответы [ 4 ]

3 голосов
/ 16 марта 2012

Во-первых, как упомянул watbywbarif, вы не должны сравнивать последовательности с помощью ==, это не работает.

Но даже если вы сравниваете массивы правильно (например, с помощью SequenceEquals() или простоглядя на них), они не всегда одинаковы.Это может произойти, если x является недопустимой строкой в ​​кодировке UTF-8.

Например, 1-байтовая последовательность 0xFF не является допустимой UTF-8.Так что же возвращает Encoding.UTF8.GetString(new byte[] { 0xFF })?Это �, U + FFFD, ЗАМЕНА ХАРАКТЕР.И, конечно, если вы позвоните по этому поводу Encoding.UTF8.GetBytes(), это не вернет вам 0xFF.

1 голос
/ 08 августа 2016

Под другим углом зрения следует, что Encoding классы предназначены для данных в оба конца, но данные, для которых они предназначены для передачи в оба конца, - это char данные, закодированные в byte, а не наоборот. Это означает, что в пределах возможностей рассматриваемого Encoding каждое значение char имеет соответствующую кодировку в значениях byte (1 или более), которые снова превращаются в одно и то же значение char. (Стоит отметить, что не все Encoding s могут сделать это для всех возможных char значений - например, Encoding.ASCII может поддерживать только char значения в диапазоне [0, 128). )

Итак, если вы начинаете с символьных данных и вам нужен способ сохранить или отправить их на носителе, который работает с байтами (например, файл на диске или сетевой поток), Encoding является отличным способом преобразовать данные char в данные byte и затем снова вернуться на другой конец. (Если вы хотите поддерживать все возможные строки, вам нужно использовать одну из Encoding s на основе Unicode, например Encoding.Unicode или Encoding.UTF8.)

Итак, что это значит, если вы начинаете с группы byte с? Ну, в зависимости от рассматриваемой кодировки, byte, с которыми вы работаете, могут на самом деле не быть последовательностью, которую Encoding когда-либо будет выводить. Вам нужно рассматривать Encoding.GetBytes как операцию кодирования и Encoding.GetChars / Encoding.GetString как операцию декодирования , и поэтому вы начинаете с произвольного массива байтов и пытается декодировать их.

Для аналогии рассмотрим формат файла JPEG для изображений. Это имеет аналогичный тип кодирования и декодирования , где в этом случае декодированные данные - это не string, а изображение. Итак, если вы берете произвольную строку байтов, каковы шансы, что она может быть декодирована как изображение JPEG? Ответ на это, очевидно, очень, очень тонкий. Скорее всего, ваши байты будут в конечном итоге идти по пути в декодере, который говорит: «Ух ты, я не ожидал, что этот байт последует за этим другим», и он сделает все возможное, чтобы обработать данные при условии что это допустимый файл JPEG, который каким-то образом поврежден.

Точно так же происходит и при преобразовании произвольного массива байтов в строку. Кодировка UTF-8 имеет особые правила о том, как кодируются значения char 128 и выше, и одно из этих правил гласит, что вы когда-нибудь увидите байт, соответствующий битовой комбинации 10xxxxxx, после байта, который соответствует шаблону, подобному * 1052. *, 1110xxxx или 11110xxx, который «вводит» многобайтовую последовательность (несколько byte s, представляющих один char). Таким образом, если ваши данные содержат байт, соответствующий шаблону 10xxxxxx, который не следует за одним из ожидаемых «вводчиков», кодировщик может только предполагать, что данные каким-то образом повреждены. Что оно делает? Он вставляет символ, который говорит: «Что-то пошло не так с закодированными данными. Я старался изо всех сил. Вот где все пошло не так». Люди, которые разработали Unicode, предвидели этот точный сценарий и создали персонажа с таким точным значением: Заменяющий персонаж .

Итак, если вы пытаетесь совершить круговую передачу ваших byte с в строке char с, и этот сценарий встречается, фактическое значение оскорбительного byte теряется, и вместо него заменяет символ вставлен. Когда вы пытаетесь превратить string обратно в массив byte, он в конечном итоге кодирует символ замены, а не исходные данные. Исходные данные потеряны.

То, что вы ищете, это отношения кодирования и декодирования, которые работают в другом направлении. Encoding предназначен для получения char данных и поиска способа временно сохранить их как byte данных. Если вы хотите взять данные byte и найти способ временно сохранить их как данные char, вам нужна кодировка, разработанная для этой конкретной цели. К счастью, они существуют. В Википедии есть довольно полный список вариантов. : -)

В .NET Framework самым простым и доступным вариантом является кодировка MIME Base-64, доступная через Convert.ToBase64String и Convert.FromBase64String.

1 голос
/ 16 марта 2012

Это потому, что == не будет сравнивать каждый элемент массива.Это не имеет никакого отношения к Encoding.UTF8.Проверьте это:

var a = new byte[] { 1 };
var b = new byte[] { 1 };
bool res = a == b;
1 голос
/ 16 марта 2012

Кодировки символов (в частности, UTF8) могут иметь разные формы для одной и той же кодовой точки.

Таким образом, при преобразовании в строку и обратно фактические байты могут представлять другую (каноническую) форму .

См. Также String.Normalize(NormalizationForm.System.Text.NormalizationForm.FormD)

См. Также:

Некоторые последовательности Юникодасчитается эквивалентным, потому что они представляют один и тот же символ.Например, следующие значения считаются эквивалентными, поскольку любое из них может использоваться для представления «ắ»:

"\u1EAF" 
"\u0103\u0301" 
"\u0061\u0306\u0301" 

Однако при порядковом, то есть двоичном, сравнении эти последовательности считаются разными, поскольку они содержат другой код Unicode.ценности.Перед выполнением порядкового сравнения приложения должны нормализовать эти строки, чтобы разложить их на базовые компоненты.

На этой странице представлен хороший пример, показывающий, какие кодировки всегда нормализуются

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