2012-07-02 121 views
7

Tôi có chuỗi hiển thị các ký tự được mã hóa UTF-8 và tôi muốn chuyển đổi nó thành Unicode.Làm thế nào để chuyển đổi một chuỗi UTF-8 thành Unicode?

Cho đến nay, thực hiện của tôi như sau:

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

tôi đang chơi với chữ "déjà". Tôi đã chuyển đổi nó thành UTF-8 thông qua online tool này, và vì vậy tôi bắt đầu thử nghiệm phương pháp của mình với chuỗi "déjÃ".

Thật không may, với việc triển khai này, chuỗi chỉ giữ nguyên.

Tôi đang ở đâu sai?

+12

Đó không phải là chuỗi UTF8. Đó là một chuỗi bị hỏng đã bị chuyển đổi nặng từ byte bằng cách sử dụng mã hóa sai. – spender

+24

UTF-8 * là * Unicode. –

+2

Chuỗi nguồn không hợp lệ UTF-8. – alexn

Trả lời

11

Vì vậy, vấn đề là giá trị đơn vị mã UTF-8 đã được lưu trữ dưới dạng chuỗi các đơn vị mã 16 bit trong C# string. Bạn chỉ cần xác minh rằng mỗi đơn vị mã nằm trong phạm vi của một byte, sao chép các giá trị đó thành các byte và sau đó chuyển đổi chuỗi byte UTF-8 mới thành 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à 

Điều này thật dễ dàng, tuy nhiên tốt nhất nên tìm nguyên nhân gốc; vị trí nơi ai đó đang sao chép đơn vị mã UTF-8 thành các đơn vị mã 16 bit. Thủ phạm có khả năng là ai đó chuyển đổi byte thành C# string bằng cách sử dụng mã hóa sai. Ví dụ. Encoding.Default.GetString(utf8Bytes, 0, utf8Bytes.Length).

Hoặc, nếu bạn chắc chắn rằng bạn biết mã hóa không chính xác được sử dụng để tạo chuỗi và việc chuyển đổi mã hóa không chính xác là không mất (thường là trường hợp mã hóa không chính xác là mã hóa byte đơn), thì bạn chỉ có thể làm các bước mã hóa ngược để có được UTF-8 gốc dữ liệu, và sau đó bạn có thể thực hiện chuyển đổi chính xác từ 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

Cảm ơn barnes53 điều này chính xác trả lời câu hỏi của tôi vì nó tạo ra kết quả tôi mong đợi. Bạn có thể tìm hiểu ý tôi từ câu hỏi khó hiểu của tôi. – remio

8

Tôi có chuỗi hiển thị ký tự UTF-8 mã hóa

Không có những điều như vậy trong .NET. Lớp chuỗi chỉ có thể lưu trữ các chuỗi trong mã hóa UTF-16. Một chuỗi được mã hóa UTF-8 chỉ có thể tồn tại dưới dạng một byte []. Cố gắng lưu trữ các byte vào một chuỗi sẽ không đến một kết thúc tốt đẹp; UTF-8 sử dụng các giá trị byte không có một codepoint Unicode hợp lệ. Nội dung sẽ bị hủy khi chuỗi được chuẩn hóa. Vì vậy, đã quá muộn để khôi phục chuỗi khi thời gian DecodeFromUtf8() của bạn bắt đầu chạy.

Chỉ xử lý văn bản được mã hóa UTF-8 với byte []. Và sử dụng UTF8Encoding.GetString() để chuyển đổi nó.

+0

Bạn chỉ ra sự nhầm lẫn mà tôi muốn tránh. Chuỗi của tôi là một chuỗi unicode, cũng là một chuỗi .Net, mà trình gỡ rối hiển thị dưới dạng 'dà © jÃ'. Do đó, mục tiêu của tôi là lấy một chuỗi (.Net) khác sẽ được hiển thị dưới dạng 'déjà' (trong trình gỡ lỗi). – remio

+1

Bạn đang thiếu điểm của câu trả lời, không có cách nào để thực hiện công việc này đúng cách cho * mọi * chuỗi mã hóa utf-8 có thể. Rằng bạn có thể làm cho nó làm việc cho các © chỉ là trùng hợp ngẫu nhiên. Rằng bạn đã gặp rắc rối với nó nên là một gợi ý, có một không gian thêm sau khi à cuối cùng. Một đặc biệt, một không gian không phá vỡ, mã điểm U + 00a0. Điều này ngẫu nhiên là một điểm mã Unicode hợp lệ. –

+0

Cảm ơn, tôi nghĩ tôi đã hiểu. Bạn có nghĩa là tôi không thể sử dụng 'chuỗi' để lưu trữ các byte UTF-8. Tuy nhiên, như bạn đề cập đến nó có thể làm việc do tai nạn, nó sẽ là một trợ giúp tuyệt vời nếu tôi có thể làm cho tai nạn làm việc. Nói cách khác, tôi vẫn không biết cách thực hiện chuyển đổi này trong các trường hợp nó sẽ hoạt động. – remio

2

những gì bạn có vẻ là một string sai được giải mã từ một mã hóa, có thể là code page 1252, mặc định là Windows của Hoa Kỳ. Dưới đây là cách đảo ngược, giả sử không có tổn thất nào khác. Một mất mát không rõ ràng ngay lập tức là non-breaking space (U + 00A0) ở cuối chuỗi của bạn không được hiển thị. Tất nhiên nó sẽ là tốt hơn để đọc nguồn dữ liệu một cách chính xác ở nơi đầu tiên, nhưng có lẽ nguồn dữ liệu đã được lưu trữ không chính xác để bắt đầu.

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

Kết quả:

déjà 
9

Nếu bạn có một chuỗi UTF-8, nơi mỗi byte là đúng ('o' -> [195, 0], [150, 0]), bạn có thể sử dụng như sau:

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

Trong trường hợp của tôi là kết quả DLL là một chuỗi UTF-8 quá, nhưng tiếc là chuỗi UTF-8 được giải thích với UTF-16 mã hóa ('o' -> [195, 0 ], [19, 32]). Vì vậy, ANSI '-' mà là 150 đã được chuyển đổi sang UTF-16 '-' mà là 8211. Nếu bạn có trường hợp này cũng vậy, bạn có thể sử dụng sau đây thay vì:

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

Hoặc Native-Phương pháp :

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

Nếu bạn cần theo cách khác, hãy xem Utf16ToUtf8. Hy vọng tôi có thể giúp đỡ.

+0

Chỉ cần chắc chắn: Chuỗi sau khi chuyển đổi sẽ vẫn là UTF-16, nó chỉ chứa dữ liệu mã hóa UTF-8. Bạn không thể xử lý chuỗi bằng cách sử dụng mã hóa UTF-8, vì .NET sẽ luôn sử dụng mã hóa UTF-16 để xử lý chuỗi. – MEN

Các vấn đề liên quan