Как преобразовать строку UTF-8 в Unicode?

у меня есть строка, которая отображает символы в кодировке UTF-8, и я хочу преобразовать ее обратно в Unicode.

на данный момент моя реализация заключается в следующем:

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

Я играю со словом "déjà". Я преобразовал его в UTF-8 через этот онлайн, и поэтому я начал тестировать свой метод со строкой "déjÃ".

к сожалению, с этой реализацией строка просто остается прежней.

где я не так?

4 ответов


таким образом, проблема заключается в том, что значения единиц кода UTF-8 были сохранены как последовательность 16-битных единиц кода в C# string. Вам просто нужно проверить, что каждый блок кода находится в пределах байта, скопировать эти значения в байты, а затем преобразовать новую последовательность байтов UTF-8 в 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à

это легко, однако было бы лучше найти первопричину; место, где кто-то копирует кодовые единицы UTF-8 в 16-битные кодовые единицы. Вероятный виновник-кто-то преобразование байтов в C# string использование неверной кодировки. Е. Г. Encoding.Default.GetString(utf8Bytes, 0, utf8Bytes.Length).


альтернативно, если вы уверены, что знаете неправильную кодировку, которая использовалась для создания строки, и что неправильное преобразование кодировки было без потерь (обычно, если неправильная кодировка является однобайтовой кодировкой), то вы можете просто сделать шаг обратной кодировки, чтобы получить исходные данные UTF-8, а затем вы можете сделать правильное преобразование из 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);

Если у вас есть строка UTF-8, где каждый байт правильный ('Ö' -> [195, 0] , [150, 0]), вы можете использовать следующее:

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

В моем случае результат DLL также является строкой UTF-8, но, к сожалению, строка UTF-8 интерпретируется с кодировкой UTF-16 ('Ö' -> [195, 0], [19, 32]). Таким образом, ANSI'–', который равен 150, был преобразован в UTF-16'–', который равен 8211. Если у вас тоже есть этот случай, вы можете использовать следующее:

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

или Родной-Метод:

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

Если вам это нужно наоборот, см. Utf16ToUtf8. Надеюсь, я смогу помочь.


У меня есть строка, которая отображает UTF-8 закодированные символы

такой вещи нет .Сеть. Класс string может хранить строки только в кодировке UTF-16. Строка в кодировке UTF-8 может существовать только как байт[]. Попытка сохранить байты в строку не приведет к хорошему концу; UTF-8 использует байтовые значения, которые не имеют допустимой кодовой точки Unicode. Содержимое будет уничтожено при нормализации строки. Так что уже слишком поздно, чтобы восстановить строку к тому времени ваш DecodeFromUtf8() начинает работать.

обрабатывать только кодированный текст UTF-8 с байтом[]. И использовать utf8encoding дополнительно.GetString (), чтобы конвертировать его.


что вы, кажется string неправильно декодирован из другой кодировки, вероятно кодовая страница 1252, который является US Windows по умолчанию. Вот как повернуть вспять, не предполагая никаких других потерь. Одна потеря не сразу это non-breaking space (от U+00A0) в конце строки, которая не отображается. Конечно, было бы лучше сначала правильно прочитать источник данных, но, возможно, источник данных был сохранен неправильно, чтобы начать с.

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

результат:

déjà