2012-07-02 38 views
7

Ho una stringa che mostra caratteri codificati in UTF-8 e voglio riconvertirli in Unicode.Come convertire una stringa UTF-8 in Unicode?

Per ora, la mia applicazione è il seguente:

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

sto giocando con la parola "déjà". L'ho convertito in UTF-8 attraverso questo online tool e così ho iniziato a testare il mio metodo con la stringa "déjÃ".

Sfortunatamente, con questa implementazione la stringa rimane la stessa.

Dove mi sbaglio?

+12

Questa non è una stringa UTF8. Questa è una stringa danneggiata che è stata malamente convertita da byte usando la codifica sbagliata. – spender

+24

UTF-8 * è * Unicode. –

+2

La stringa di origine non è UTF-8 valida. – alexn

risposta

11

Quindi il problema è che i valori dell'unità di codice UTF-8 sono stati memorizzati come una sequenza di unità di codice a 16 bit in un C# string. È sufficiente verificare che ogni unità di codice rientri nell'intervallo di un byte, copiare quei valori in byte e quindi convertire la nuova sequenza di byte UTF-8 in 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à 

Questo è facile, tuttavia sarebbe meglio trovare la causa principale; la posizione in cui qualcuno sta copiando unità di codice UTF-8 in unità di codice a 16 bit. Il probabile colpevole è qualcuno che converta i byte in un C# string usando la codifica sbagliata. Per esempio. Encoding.Default.GetString(utf8Bytes, 0, utf8Bytes.Length).


In alternativa, se si è sicuri di sapere la codifica non corretta che è stato utilizzato per produrre la stringa, e che codifica la trasformazione non corretto senza perdita di dati (di solito il caso se la codifica non corretta è una singola codifica byte), allora si può semplicemente fare il passo di codifica inversa per ottenere i dati UTF-8 originali, e allora si può fare la conversione corretto dal byte 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

Grazie barnes53 questo risponde esattamente alla mia domanda in quanto produce il risultato che mi aspetto. Potresti scoprire cosa intendevo dalla mia domanda confusa. – remio

8

devo stringa che visualizza i caratteri UTF-8 codificati

Non v'è nulla di simile in .NET. La classe stringa può memorizzare solo stringhe nella codifica UTF-16. Una stringa con codifica UTF-8 può esistere solo come byte []. Cercando di memorizzare i byte in una stringa non verrà una buona fine; UTF-8 utilizza valori di byte che non hanno un punto di codice Unicode valido. Il contenuto verrà distrutto quando la stringa viene normalizzata. Quindi è già troppo tardi per recuperare la stringa quando decode l'esecuzione di DecodeFromUtf8().

Gestire solo testo con codifica UTF-8 con byte []. E usa UTF8Encoding.GetString() per convertirlo.

+0

Hai sottolineato la confusione che volevo evitare. La mia stringa è una stringa unicode, beh è una stringa .Net, che il debugger visualizza come 'dà © jÃ'. Quindi, il mio obiettivo è ottenere un'altra stringa (.Net) che verrà visualizzata come 'déjà' (nel debugger, per esempio). – remio

+1

Ti manca il punto della risposta, non c'è modo di farlo funzionare correttamente per * ogni * possibile stringa codificata utf-8. Il fatto che tu possa farlo funzionare per déjà è solo una coincidenza. Che tu stia già avendo problemi con questo dovrebbe essere un suggerimento, c'è uno spazio in più dopo l'ultimo Uno speciale, uno spazio senza interruzioni, punto di codice U + 00a0. Che per caso è un codice Unicode valido. –

+0

Grazie, penso di averlo capito. Vuoi dire che non posso usare 'string' per memorizzare i byte UTF-8. Tuttavia, come dici che potrebbe funzionare per sbaglio, sarebbe di grande aiuto se potessi far funzionare gli incidenti. In altre parole, ancora non so come fare questa conversione nei casi in cui avrebbe funzionato. – remio

2

Quello che devi sembra essere una string erroneamente decodificato da un altro codifica, probabilmente code page 1252, che è l'impostazione predefinita di Windows. Ecco come invertire, non assumendo altre perdite. Una perdita non immediatamente evidente è la non-breaking space (U + 00A0) alla fine della stringa che non viene visualizzata. Naturalmente sarebbe meglio leggere correttamente l'origine dati, ma forse l'origine dei dati è stata memorizzata in modo errato per cominciare.

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

Risultato:

déjà 
9

Se si dispone di una stringa UTF-8, dove ogni byte è corretta ('O' -> [195, 0], [150, 0]), è possono utilizzare i seguenti:

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

Nel mio caso il risultato DLL è una stringa UTF-8 anche, ma purtroppo la stringa UTF-8 è interpretato con codifica UTF-16 ('O' -> [195, 0 ], [19, 32]). Così l'ANSI '-', che è di 150 è stato convertito alla UTF-16 '-', che è 8211. Se si dispone di questo caso, è possibile utilizzare il seguente comando:

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 il Native-Metodo :

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

Se necessario, viceversa, vedere Utf16ToUtf8. Spero di poter essere di aiuto.

+0

Solo per essere sicuri: la stringa dopo la conversione sarà ancora UTF-16, contiene solo dati di codifica UTF-8. Non è possibile gestire le stringhe utilizzando la codifica UTF-8, poiché .NET utilizzerà sempre la codifica UTF-16 per gestire le stringhe. – MEN

Problemi correlati