2012-07-02 15 views
7

Mam ciąg znaków wyświetlający znaki zakodowane w UTF-8 i chcę go przekonwertować z powrotem na kod Unicode.Jak przekonwertować ciąg znaków UTF-8 na kod Unicode?

Na razie moja realizacja jest następujący:

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

gram ze słowem "déjà". Przekształciłem go w UTF-8 przez to online tool, a więc zacząłem testować moją metodę za pomocą ciągu "déjÃ".

Niestety, przy tej implementacji ciąg pozostaje taki sam.

Gdzie ja się mylę?

+12

To nie jest ciąg znaków UTF8. Jest to uszkodzony ciąg, który został źle przekonwertowany z bajtów przy użyciu niewłaściwego kodowania. – spender

+24

UTF-8 * to * Unikod. –

+2

Ciąg źródłowy jest nieprawidłowy UTF-8. – alexn

Odpowiedz

11

Problem polega na tym, że wartości jednostki kodowej UTF-8 zostały zapisane jako sekwencja 16-bitowych jednostek kodu w C# string. Po prostu musisz sprawdzić, czy każda jednostka kodu znajduje się w zakresie bajta, skopiuj te wartości do bajtów, a następnie przekonwertuj nową sekwencję bajtów UTF-8 na 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à 

Jest to łatwe, ale najlepiej byłoby znaleźć przyczynę; miejsce, w którym ktoś kopiuje jednostki kodowe UTF-8 do 16-bitowych jednostek kodu. Prawdopodobnym winowajcą jest ktoś, kto konwertuje bajty na C# string, używając niewłaściwego kodowania. Na przykład. Encoding.Default.GetString(utf8Bytes, 0, utf8Bytes.Length).


Ewentualnie, jeśli jesteś pewien, że wiesz niepoprawne kodowanie, który został zastosowany do wytworzenia ciąg, i że błędne kodowanie była transformacja bezstratny (zwykle w przypadku, gdy błędne kodowanie jest jedno kodowanie bajt), a następnie można po prostu zrobić krok kodowania odwrotny do uzyskania oryginalnej UTF-8 danych, a następnie można zrobić poprawną konwersję z UTF-8 bajtów:

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

Dzięki barnes53 to dokładnie odpowiada na moje pytanie, ponieważ daje oczekiwany rezultat. Możesz dowiedzieć się, co mam na myśli, od mojego mylącego pytania. – remio

8

Mam ciąg znaków, który wyświetla kodowanie UTF-8 znaków

Nie ma czegoś takiego w .NET. Klasa string może przechowywać tylko ciągi w kodowaniu UTF-16. Łańcuch kodowany w UTF-8 może istnieć tylko jako bajt []. Próba zapisania bajtów w ciągu znaków nie zakończy się pomyślnie; UTF-8 używa wartości bajtów, które nie mają prawidłowego codepoint Unicode. Treść zostanie zniszczona, gdy struna zostanie znormalizowana. Tak więc jest już za późno, aby odzyskać ciąg znaków, zanim DecodeFromUtf8() zacznie działać.

Obsługuj tylko kodowany w UTF-8 tekst z bajtem []. I użyj UTF8Encoding.GetString(), aby go przekonwertować.

+0

Wskazałeś na zamieszanie, którego chciałem uniknąć. Mój ciąg jest ciągiem znaków Unicode, a także ciągiem .Net, który debugger wyświetla jako "dà © jÃ". Dlatego moim celem jest uzyskanie innego ciągu (.Net), który będzie wyświetlany jako 'déjà' (na przykład w debugerze). – remio

+1

Brakuje punktu odpowiedzi, nie ma sposobu, aby to działało poprawnie dla * każdego * możliwego zakodowanego ciągu UTF-8. To, że możesz sprawić, że działa na dà © jà is jest tylko zbiegiem okoliczności. To, że już masz z tym problemy, powinno być jedną wskazówką, jest dodatkowa przestrzeń po ostatnim Ã. Specjalna, nie łamająca się przestrzeń, kod punktu U + 00a0. Który jest przypadkiem prawidłowym punktem kodowym Unicode. –

+0

Dzięki, myślę, że rozumiem. Masz na myśli, że po prostu nie mogę użyć 'string' do przechowywania bajtów UTF-8. Jednakże, jak wspomniałeś, może to działać przez przypadek, byłoby to wielką pomocą, gdybym mógł sprawić, żeby wypadki zadziałały. Innymi słowy, nadal nie wiem, jak wykonać tę konwersję w przypadkach, w których zadziała. – remio

2

Co masz zdaje się być string błędnie dekodowane z innego kodowanie, prawdopodobnie code page 1252, co jest standardem US Windows. Oto jak się odwrócić, zakładając, że nie ma żadnej innej straty. Jedną stratą, która nie jest od razu widoczna, jest non-breaking space (U + 00A0) na końcu łańcucha, który nie jest wyświetlany. Oczywiście lepiej byłoby najpierw poprawnie odczytać źródło danych, ale być może źródło danych było niepoprawnie zapisane na początku.

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

Wynik:

déjà 
9

Jeśli masz UTF-8 znaków, gdzie każdy bajt jest prawidłowe ('o' -> [195, 0], [150, 0]), ty można użyć następujących:

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

W moim przypadku efekt DLL jest UTF-8 ciąg też, ale niestety ciąg UTF-8 jest interpretowana z UTF-16 kodowania ('o' -> [195, 0 ], [19, 32]). Więc ANSI „-”, który znajduje się 150 przekształcono w UTF-16 „-”, który jest 8211. Jeśli masz tym przypadku można użyć następujących zamiast:

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

Or Native-method :

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

Jeśli potrzebujesz go w inny sposób, zobacz Utf16ToUtf8. Mam nadzieję, że mogę pomóc.

+0

Tylko dla pewności: Ciąg po konwertowaniu nadal będzie UTF-16, zawiera tylko dane kodowania UTF-8. Nie można obsługiwać ciągów przy użyciu kodowania UTF-8, ponieważ .NET będzie zawsze używać kodowania UTF-16 do obsługi ciągów. – MEN

Powiązane problemy