2012-07-02 3 views
7

J'ai une chaîne qui affiche les caractères codés UTF-8, et je veux le convertir en Unicode.Comment convertir une chaîne UTF-8 en Unicode?

Pour l'instant, ma mise en œuvre est la suivante:

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); 
} 

Je joue avec le mot "déjà". Je l'ai converti en UTF-8 à travers ce online tool, et j'ai donc commencé à tester ma méthode avec la chaîne "déjÃ".

Malheureusement, avec cette implémentation, la chaîne reste la même.

Où est-ce que je me trompe?

+12

Ce n'est pas une chaîne UTF8. C'est une chaîne corrompue qui a été mal convertie à partir d'octets en utilisant le mauvais encodage. – spender

+24

UTF-8 * est * Unicode. –

+2

La chaîne source est invalide UTF-8. – alexn

Répondre

11

Ainsi, le problème est que les valeurs d'unité de code UTF-8 ont été stockées sous la forme d'une séquence d'unités de code de 16 bits dans un C# string. Vous devez simplement vérifier que chaque unité de code est dans la plage d'un octet, copier ces valeurs en octets, puis convertir la nouvelle séquence d'octets 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à 

Ceci est facile, mais il serait préférable de trouver la cause première; l'emplacement où quelqu'un copie des unités de code UTF-8 dans des unités de code de 16 bits. Le coupable probable est quelqu'un convertissant des octets dans un C# string en utilisant le mauvais encodage. Par exemple. Encoding.Default.GetString(utf8Bytes, 0, utf8Bytes.Length).


Sinon, si vous êtes sûr que vous connaissez le codage incorrect qui a été utilisé pour produire la chaîne, et que la transformation d'encodage incorrect a été sans perte (généralement le cas si le codage incorrect est un codage d'un seul octet), puis vous pouvez simplement faire l'étape de codage inverse pour obtenir les UTF-8 données originales, et vous pouvez faire la conversion correcte de UTF-8 octets:

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

Merci barnes53 cela répond exactement à ma question car il produit le résultat que j'attends. Vous pourriez trouver ce que je voulais dire à partir de ma question confuse. – remio

8

J'ai chaîne qui affiche des caractères UTF-8 codé

Il n'y a pas une telle chose dans .NET. La classe de chaînes ne peut stocker que des chaînes au format UTF-16. Une chaîne codée en UTF-8 ne peut exister que sous la forme d'un octet []. Essayer de stocker des octets dans une chaîne ne sera pas une bonne fin; UTF-8 utilise des valeurs d'octets qui n'ont pas de code codé Unicode valide. Le contenu sera détruit lorsque la chaîne est normalisée. Il est donc déjà trop tard pour récupérer la chaîne au moment où votre DecodeFromUtf8() démarre.

Ne manipulez que le texte codé en UTF-8 avec octet []. Et utilisez UTF8Encoding.GetString() pour le convertir.

+0

Vous avez souligné la confusion que je voulais éviter. Ma chaîne est une chaîne unicode, bien une chaîne .Net, que le débogueur affiche comme étant «déchu». Par conséquent, mon but est d'obtenir une autre chaîne (.Net) qui sera affichée comme 'déjà' (dans le débogueur, par exemple). – remio

+1

Il vous manque le point de la réponse, il n'y a aucun moyen de faire ce travail correctement pour * chaque * chaîne encodée utf-8 possible. Que vous pourriez le faire fonctionner pour le jour est simplement une coïncidence. Que vous avez déjà des problèmes avec cela devrait être un indice, il y a un espace supplémentaire après le dernier. Un spécial, un espace insécable, code U + 00a0. Ce qui arrive à être un point de code Unicode valide par accident. –

+0

Merci, je pense que je comprends. Vous voulez dire que je ne peux pas utiliser 'string' pour stocker les octets UTF-8. Cependant, comme vous le dites, cela pourrait fonctionner par accident, ce serait une aide précieuse si je pouvais faire fonctionner les accidents. En d'autres termes, je ne sais toujours pas comment faire cette conversion dans les cas où cela fonctionnerait. – remio

2

Ce que vous avez semble être un string mal décodé d'un autre codage, probablement code page 1252, qui est la valeur par défaut de Windows US. Voici comment inverser, en supposant aucune autre perte. Une perte qui n'est pas immédiatement visible est le non-breaking space (U + 00A0) à la fin de votre chaîne qui n'est pas affichée. Bien sûr, il serait préférable de lire la source de données correctement en premier lieu, mais peut-être que la source de données a été mal stockée pour commencer.

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); 
    } 
} 

Résultat:

déjà 
9

Si vous avez une chaîne UTF-8, où chaque octet est correct ('Ö' -> [195, 0], [150, 0]), vous peut utiliser les éléments suivants:

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()); 
} 

Dans mon cas, le résultat de DLL est une chaîne UTF-8 aussi, mais malheureusement la chaîne UTF-8 est interprété avec codage UTF-16 ('Ö' -> [195, 0 ], [19, 32]). Ainsi, la norme ANSI « - » qui est de 150 a été converti en UTF-16 « - » qui est 8211. Si vous avez ce cas aussi, vous pouvez utiliser ce qui suit à la place:

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); 
} 

Ou le natif-Méthode :

[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 vous en avez besoin l'inverse, voir Utf16ToUtf8. J'espère que je pourrais être utile.

+0

Juste pour être sûr: La chaîne après la conversion sera toujours UTF-16, il contient juste des données de codage UTF-8. Vous ne pouvez pas gérer les chaînes à l'aide de l'encodage UTF-8, car .NET utilisera toujours l'encodage UTF-16 pour gérer les chaînes. – MEN

Questions connexes