2012-07-02 26 views
7

Tengo una cadena que muestra caracteres codificados en UTF-8, y quiero convertirla nuevamente a Unicode.Cómo convertir una cadena UTF-8 en Unicode?

Por ahora, mi aplicación es el siguiente:

public static string DecodeFromUtf8(this string utf8String) 
{ 
    // read the string as UTF-8 bytes. 
    byte[] encodedBytes = Encoding.UTF8.GetBytes(utf8String); 

    // convert them into unicode bytes. 
    byte[] unicodeBytes = Encoding.Convert(Encoding.UTF8, Encoding.Unicode, encodedBytes); 

    // builds the converted string. 
    return Encoding.Unicode.GetString(encodedBytes); 
} 

estoy jugando con la palabra "déjà". Lo he convertido en UTF-8 a través de este online tool, así que comencé a probar mi método con la cadena "déjÃ".

Desafortunadamente, con esta implementación, la cadena sigue siendo la misma.

¿Dónde estoy equivocado?

+12

Eso no es una cadena UTF8. Esa es una cadena corrupta que ha sido mal convertida desde bytes utilizando la codificación incorrecta. – spender

+24

UTF-8 * es * Unicode. –

+2

La cadena fuente no es válida UTF-8. – alexn

Respuesta

11

Por lo tanto, el problema es que los valores de unidad de código UTF-8 se han almacenado como una secuencia de unidades de código de 16 bits en un C# string. Simplemente necesita verificar que cada unidad de código se encuentre dentro del rango de un byte, copie esos valores en bytes y luego convierta la nueva secuencia de bytes UTF-8 en UTF-16.

public static string DecodeFromUtf8(this string utf8String) 
{ 
    // copy the string as UTF-8 bytes. 
    byte[] utf8Bytes = new byte[utf8String.Length]; 
    for (int i=0;i<utf8String.Length;++i) { 
     //Debug.Assert(0 <= utf8String[i] && utf8String[i] <= 255, "the char must be in byte's range"); 
     utf8Bytes[i] = (byte)utf8String[i]; 
    } 

    return Encoding.UTF8.GetString(utf8Bytes,0,utf8Bytes.Length); 
} 

DecodeFromUtf8("d\u00C3\u00A9j\u00C3\u00A0"); // déjà 

Esto es fácil, sin embargo, sería mejor encontrar la causa raíz; la ubicación donde alguien está copiando unidades de código UTF-8 en unidades de códigos de 16 bits. El posible culpable es alguien convirtiendo bytes en un C# string usando la codificación incorrecta. P.ej. Encoding.Default.GetString(utf8Bytes, 0, utf8Bytes.Length).


alternativa, si está seguro de que conoce la codificación incorrecta que fue utilizado para producir la cadena, y que era incorrecta transformación de codificación sin pérdidas (suele ser el caso si la codificación incorrecta es un solo byte de codificación), a continuación, sólo tiene que hacer el paso inverso de codificación para obtener los datos UTF-8 originales, y entonces usted puede hacer la conversión correcta de bytes UTF-8:

public static string UndoEncodingMistake(string mangledString, Encoding mistake, Encoding correction) 
{ 
    // the inverse of `mistake.GetString(originalBytes);` 
    byte[] originalBytes = mistake.GetBytes(mangledString); 
    return correction.GetString(originalBytes); 
} 

UndoEncodingMistake("d\u00C3\u00A9j\u00C3\u00A0", Encoding(1252), Encoding.UTF8); 
+0

Gracias barnes53 esto responde exactamente mi pregunta, ya que produce el resultado que espero. Podrías averiguar a qué me refiero con mi pregunta confusa. – remio

8

que tienen cadena que muestra caracteres UTF-8 codificado

No hay tal cosa en .NET. La clase de cadena solo puede almacenar cadenas en codificación UTF-16. Una cadena codificada en UTF-8 solo puede existir como un byte []. Intentar almacenar bytes en una cadena no tendrá un buen final; UTF-8 usa valores de bytes que no tienen un punto de código Unicode válido. El contenido se destruirá cuando la cadena esté normalizada. Por lo tanto, ya es demasiado tarde para recuperar la cadena cuando su DecodeFromUtf8() comience a ejecutarse.

Solo manipule texto codificado en UTF-8 con byte []. Y use UTF8Encoding.GetString() para convertirlo.

+0

Usted señaló la confusión que quería evitar. Mi cadena es una cadena Unicode, así es una cadena .Net, que el depurador muestra como 'dà © jÃ'. Por lo tanto, mi objetivo es obtener otra cadena (.Net) que se mostrará como 'déjà' (en el depurador, por ejemplo). – remio

+1

Falta el sentido de la respuesta, no hay forma de que esto funcione correctamente para * cada * cadena codificada en utf-8. Que puedas hacer que funcione para déjé es mera coincidencia. Que ya estés teniendo problemas con él debería ser una pista, hay un espacio extra después de la última Ã. Una especial, un espacio sin interrupción, punto de código U + 00a0. Lo que resulta ser un punto de código Unicode válido por accidente. –

+0

Gracias, creo que lo entiendo. Quiere decir que simplemente no puedo usar 'cadena' para almacenar los bytes UTF-8. Sin embargo, como usted menciona podría funcionar por accidente, sería una gran ayuda si pudiera hacer que los accidentes funcionen. En otras palabras, todavía no sé cómo hacer esta conversión en los casos en que funcionaría. – remio

2

lo que tienes parece que hay una string incorrectamente decodificado de otro codificación, probablemente code page 1252, que es el valor predeterminado de Windows de EE. UU. A continuación, le mostramos cómo revertir, asumiendo que no hay otra pérdida. Una pérdida no aparente de inmediato es non-breaking space (U + 00A0) al final de la cadena que no se muestra. Por supuesto, sería mejor leer la fuente de datos correctamente en primer lugar, pero tal vez la fuente de datos se almacenó incorrectamente para empezar.

using System; 
using System.Text; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     string junk = "déjÃ\xa0"; // Bad Unicode string 

     // Turn string back to bytes using the original, incorrect encoding. 
     byte[] bytes = Encoding.GetEncoding(1252).GetBytes(junk); 

     // Use the correct encoding this time to convert back to a string. 
     string good = Encoding.UTF8.GetString(bytes); 
     Console.WriteLine(good); 
    } 
} 

Resultado:

déjà 
9

Si usted tiene una cadena UTF-8, donde cada byte es correcta ('O' -> [195, 0], [150, 0]), se puede utilizar los siguientes:

public static string Utf8ToUtf16(string utf8String) 
{ 
    /*************************************************************** 
    * Every .NET string will store text with the UTF-16 encoding, * 
    * known as Encoding.Unicode. Other encodings may exist as  * 
    * Byte-Array or incorrectly stored with the UTF-16 encoding. * 
    *                * 
    * UTF-8 = 1 bytes per char         * 
    * ["100" for the ansi 'd']         * 
    * ["206" and "186" for the russian '?']     * 
    *                * 
    * UTF-16 = 2 bytes per char         * 
    * ["100, 0" for the ansi 'd']        * 
    * ["186, 3" for the russian '?']       * 
    *                * 
    * UTF-8 inside UTF-16           * 
    * ["100, 0" for the ansi 'd']        * 
    * ["206, 0" and "186, 0" for the russian '?']    * 
    *                * 
    * First we need to get the UTF-8 Byte-Array and remove all * 
    * 0 byte (binary 0) while doing so.       * 
    *                * 
    * Binary 0 means end of string on UTF-8 encoding while on  * 
    * UTF-16 one binary 0 does not end the string. Only if there * 
    * are 2 binary 0, than the UTF-16 encoding will end the  * 
    * string. Because of .NET we don't have to handle this.  * 
    *                * 
    * After removing binary 0 and receiving the Byte-Array, we * 
    * can use the UTF-8 encoding to string method now to get a * 
    * UTF-16 string.            * 
    *                * 
    ***************************************************************/ 

    // Get UTF-8 bytes and remove binary 0 bytes (filler) 
    List<byte> utf8Bytes = new List<byte>(utf8String.Length); 
    foreach (byte utf8Byte in utf8String) 
    { 
     // Remove binary 0 bytes (filler) 
     if (utf8Byte > 0) { 
      utf8Bytes.Add(utf8Byte); 
     } 
    } 

    // Convert UTF-8 bytes to UTF-16 string 
    return Encoding.UTF8.GetString(utf8Bytes.ToArray()); 
} 

En mi caso, el resultado DLL es una cadena UTF-8 también, pero desafortunadamente la cadena UTF-8 se interpreta con codificación UTF-16 ('O' -> [195, 0 ], [19, 32]). Así que la ANSI '-' que es 150 se convirtió en el UTF-16 '-', que es 8211. Si usted tiene este caso, puede utilizar el siguiente lugar:

public static string Utf8ToUtf16(string utf8String) 
{ 
    // Get UTF-8 bytes by reading each byte with ANSI encoding 
    byte[] utf8Bytes = Encoding.Default.GetBytes(utf8String); 

    // Convert UTF-8 bytes to UTF-16 bytes 
    byte[] utf16Bytes = Encoding.Convert(Encoding.UTF8, Encoding.Unicode, utf8Bytes); 

    // Return UTF-16 bytes as UTF-16 string 
    return Encoding.Unicode.GetString(utf16Bytes); 
} 

O el nativo-Método :

[DllImport("kernel32.dll")] 
private static extern Int32 MultiByteToWideChar(UInt32 CodePage, UInt32 dwFlags, [MarshalAs(UnmanagedType.LPStr)] String lpMultiByteStr, Int32 cbMultiByte, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpWideCharStr, Int32 cchWideChar); 

public static string Utf8ToUtf16(string utf8String) 
{ 
    Int32 iNewDataLen = MultiByteToWideChar(Convert.ToUInt32(Encoding.UTF8.CodePage), 0, utf8String, -1, null, 0); 
    if (iNewDataLen > 1) 
    { 
     StringBuilder utf16String = new StringBuilder(iNewDataLen); 
     MultiByteToWideChar(Convert.ToUInt32(Encoding.UTF8.CodePage), 0, utf8String, -1, utf16String, utf16String.Capacity); 

     return utf16String.ToString(); 
    } 
    else 
    { 
     return String.Empty; 
    } 
} 

Si lo necesita al revés, consulte Utf16ToUtf8. Espero que pueda ser de ayuda.

+0

Solo para estar seguro: la cadena después de la conversión seguirá siendo UTF-16, solo contiene datos de codificación UTF-8. No puede manejar cadenas utilizando la codificación UTF-8, porque .NET siempre usará la codificación UTF-16 para manejar cadenas. – MEN

Cuestiones relacionadas