2012-03-16 16 views
5

En .NET por qué no es cierto que:¿Por qué no es `Encoding.UTF8.GetBytes (Encoding.UTF8.GetString (x)) == x`

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

devuelve la matriz de bytes originales para una matriz de bytes arbitraria x?

Es mentioned en respuesta a otra pregunta pero el respondedor no explica por qué.

+0

La respuesta a la que vinculó habla de ASCII, no de UTF-8. – svick

+1

¿Puedes incluso comparar matrices de bytes usando '=='? Probablemente solo compare sus referencias, probablemente tendrá que hacer un ciclo para comparar cada elemento de la matriz para la igualdad. – Matthew

+0

@Matthew la esencia de [esa respuesta] (http://stackoverflow.com/a/3946274/85371) parece ser que la codificación puede variar. Y sí, el código de ejemplo está defectuoso/al revés. – sehe

Respuesta

1

Las codificaciones de caracteres (UTF8, específicamente) pueden tener diferentes formas para el mismo punto de código.

Así que cuando convierte a una cadena y viceversa, los bytes reales pueden representar un forma diferente (canónica).

Ver también String.Normalize(NormalizationForm.System.Text.NormalizationForm.FormD)

Ver también:

Algunas secuencias Unicode son contras equivalente ideado porque representan el mismo personaje. Por ejemplo, el siguiente se consideran equivalentes porque cualquiera de estos puede ser usado para representar "A":

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

Sin embargo, ordinal, es decir, binario, las comparaciones en cuenta estas secuencias diferentes porque contienen diferentes valores de código Unicode. Antes de realizar comparaciones ordinales, las aplicaciones deben normalizar estas cadenas para descomponerlas en sus componentes básicos.

esa página con una muestra agradable que le muestra lo codificaciones son siempre normalizado

+0

¿Por qué cualquiera de los dos métodos cambiaría la forma de la cadena? – svick

+0

@svick No me preguntes. No revisé la documentación para asegurarme de que no, aunque – sehe

+0

, creo que esto no sucederá. Eso se debe a que esas diferentes formas son * no * propiedad de varias codificaciones, pero de Unicode en sí mismo. Entonces, un personaje se puede representar como diferentes secuencias de puntos de código. Pero una sola secuencia de puntos de código se puede representar de una sola manera como una secuencia de bytes cuando se utiliza una codificación específica. – svick

1

Esto se debe a == No se comparará cada elemento de la matriz. No tiene conexión con Encoding.UTF8. Marque esta :

var a = new byte[] { 1 }; 
var b = new byte[] { 1 }; 
bool res = a == b; 
3

En primer lugar, como se mencionó watbywbarif, no debería comparar las secuencias mediante el uso de ==, eso no funciona.

Pero incluso si compara las matrices correctamente (por ejemplo, usando SequenceEquals() o simplemente mirándolas), no son siempre las mismas. Un caso donde esto puede ocurrir es si x es una cadena codificada UTF-8 no válida.

Por ejemplo, la secuencia de 1 byte de 0xFF no es válida UTF-8. Entonces, ¿qué devuelve Encoding.UTF8.GetString(new byte[] { 0xFF })? Es , U + FFFD, CARÁCTER DE REEMPLAZO. Y, por supuesto, si llama al Encoding.UTF8.GetBytes() sobre eso, no le devuelve 0xFF.

+0

+1 de mí, lindo ejemplo – sehe

+1

No sabía sobre el método de extensión 'SequenceEqual', muy útil. – PyreneesJim

1

Otro ángulo para llegar a esto desde los Encoding es que las clases son diseñado para los datos de ida y vuelta, pero los datos que están diseñados para ida y vuelta es char datos, codificado a byte, no al revés .Lo que esto significa es que, dentro de las capacidades del Encoding en cuestión, cada valor char tiene una codificación correspondiente en byte valores (1 o más) que volverán a ser exactamente el mismo valor char. (Vale la pena señalar que no todos los Encoding s pueden hacer esto para todos los posibles valores char - por ejemplo, sólo puede soportar char valores en el rango [0, 128).)

lo tanto, si usted está comenzando con carácter datos y necesita una forma de almacenarlos o enviarlos en un medio que funcione con bytes (como un archivo en un disco o una transmisión en red), Encoding es una forma excelente de convertir los datos de char a byte datos y luego nuevamente en el Otro final. (Si desea apoyar a todos los posibles cuerdas, tendrá que utilizar una de las basadas en Unicode Encoding s, como Encoding.Unicode o Encoding.UTF8.)

Entonces, ¿qué quiere decir esto que si estás empezando con un grupo de byte s? Bueno, dependiendo de la codificación en cuestión, el byte s con el que está trabajando podría no ser realmente una secuencia que Encoding haya tenido alguna vez salida. Usted necesita mirar Encoding.GetBytes como operación codificación y Encoding.GetChars/Encoding.GetString como decodificación operación, y así vas a empezar con una serie arbitraria de bytes e intentando decodificar ellos.

Para una analogía, considere el formato de archivo JPEG para las imágenes. Esto tiene un tipo similar de codificación y decodificación, donde en este caso los datos decodificados no son string sino una imagen. Entonces, si toma una cadena de bytes arbitraria, ¿cuáles son las posibilidades de que pueda decodificarse como una imagen JPEG? La respuesta a eso, obviamente, es muy muy delgada. Lo más probable es que tus bytes terminen yendo por un camino en el decodificador que diga: "De acuerdo, no esperaba que ese byte venga después de ese otro", y hará todo lo posible para manejar los datos en el supuesto que es un archivo JPEG válido que se dañó de alguna manera.

Exactamente lo mismo sucede cuando convierte una matriz arbitraria de bytes en una cadena. La codificación UTF-8 tiene reglas específicas sobre cómo se codifican los valores char y una de esas reglas dice que solo verá un byte que coincida con el patrón de bits 10xxxxxx después de uno que coincida con un patrón como 110xxxxx, 1110xxxx o 11110xxx, que "introduce" una secuencia de varios bytes (múltiples byte s que representan un solo char). Entonces, si sus datos contienen un byte que coincida con el patrón 10xxxxxx que no hace siga uno de los "introductores" esperados, el codificador solo puede asumir que los datos se dañaron de alguna manera. ¿Qué hace? Inserta un personaje que dice: "Algo salió terriblemente mal con los datos codificados. Lo intenté lo mejor posible. Aquí es donde salió mal". Las personas que diseñaron Unicode anticiparon este escenario exacto y crearon un personaje con este significado preciso: el Replacement Character.

lo tanto, si usted está tratando de ida y vuelta a sus byte s de una serie de char s y se encuentra este escenario, el valor real del infractor byte se pierde, y en su lugar se inserta un carácter de reemplazo. Cuando intenta convertir string en una matriz byte, termina codificando el carácter de reemplazo, no los datos originales. La información original está perdida.

Lo que está buscando es una relación de decodificación de codificación & que funciona en la otra dirección. Encoding es para tomar los datos de char y encontrar la forma de almacenarlos temporalmente como datos byte. Si desea tomar byte datos y encontrar una forma de almacenarlo temporalmente como datos char, necesita una codificación diseñada para ese propósito específico. Afortunadamente, estos existen. Wikipedia tiene un fairly comprehensive list de las opciones. :-)

Dentro de .NET Framework, la opción más simple y accesible es la codificación MIME Base-64, que se expone a través de Convert.ToBase64String y Convert.FromBase64String.

Cuestiones relacionadas