Преобразование из ASCII в код символа Unicode (FreeType2)

Я использую FreeType2 в одном из своих проектов. Чтобы отобразить письмо, мне нужно предоставить двухбайтовый код символа Unicode. Коды символов, которые читает программа, имеют однобайтовый формат ASCII. Это не представляет проблемы для кодов символов ниже 128 (коды символов такие же), но другие 128 не совпадают. Например:

'a' в ASCII - это 0x61, 'a' в Unicode - это 0x0061 - это нормально
'ą' в ASCII - это 0xB9, 'ą' в Unicode - 0x0105 - совершенно другое

Я пытался использовать там функции WinAPI, но, должно быть, что-то делаю не так. Вот образец:

unsigned char szTest1[] = "ąółź"; //ASCII format
wchar_t* wszTest2;
int size = MultiByteToWideChar(CP_UTF8, 0, (char*)szTest1, 4, NULL, 0);
printf("size = %d\n", size);
wszTest2 = new wchar_t[size];
MultiByteToWideChar(CP_UTF8, 0, (char*)szTest1, 4, wszTest2, size);
printf("HEX: %x\n", wszTest2[0]);
delete[] wszTest2;

Я ожидаю, что будет создана новая широкая строка без NULL в конце. Однако переменная size всегда равна 0. Есть идеи, что я делаю не так? А может есть способ решить проблему попроще?


person Tomalla    schedule 16.10.2012    source источник
comment
НЕТ КОДОВ ASCII ВЫШЕ 127! ПО ОПРЕДЕЛЕНИЮ! Простите за крик. Теперь, когда я привлек ваше внимание, вам нужно выяснить, какова фактическая кодировка вашего текста ascii и на самом деле использовать эту кодировку для его декодирования.   -  person Joachim Sauer    schedule 16.10.2012


Ответы (2)


Параметр CodePage для MultiByteToWideChar неверен. Utf-8 - это не то же самое, что ASCII. Вы должны использовать CP_ACP, который указывает текущую кодовую страницу системы (которая не совпадает с ASCII - см. различия в форматах Unicode, UTF, ASCII, ANSI)

Размер равен нулю, скорее всего, потому что ваша тестовая строка не является допустимой строкой Utf-8.

Почти для всех функций Win32 вы можете вызвать GetLastError () после того, как функция не может получить подробный код ошибки, поэтому вызов этого также даст вам более подробную информацию.

person shf301    schedule 16.10.2012
comment
Да, тестовая строка определенно недействительна. UTF-8: 0xB9 не является допустимым ведущим байтом. - person R. Martinho Fernandes; 16.10.2012
comment
Да действительно, произошла ошибка. Это была ERROR_NO_UNICODE_TRANSLATION. После изменения CP_UTF8 на CP_ACP переменная size увеличилась до 4, как и предполагалось. Более того, были возвращены правильные коды символов, так что это здорово! Также спасибо за список различий между форматами кодирования. Пригодится. Ваше здоровье - person Tomalla; 16.10.2012

«Чистый» набор символов ASCII ограничен диапазоном 0–127 (7 бит). 8-битные символы с набором наиболее значимых битов (то есть в диапазоне 128–255) не определены однозначно: их определение зависит от кодовой страницы. Итак, ваш персонаж ą (СТРОЧНАЯ ЛАТИНСКАЯ БУКВА A С ОГОНЕК) представлен значением 0xB9 на определенной кодовой странице, которая должна быть Windows-1250. На других кодовых страницах значение 0xB9 связано с другим символом (например, в кодовая страница Windows 1252, 0xB9 связан с символом ¹, т. е. с цифрой надстрочного индекса 1).

Чтобы преобразовать символы из определенной кодовой страницы в Unicode UTF-16 с помощью Windows Win32 API, вы можете использовать _ 6_, указав правильную кодовую страницу (которая не CP_UTF8, как написано в коде вашего вопроса; фактически, CP_UTF8 обозначает Unicode UTF-8). Вы можете попробовать указать 1250 (ANSI Central European; Central European (Windows)) как правильный идентификатор кодовой страницы.

Если у вас есть доступ к ATL в коде, вы можете использовать удобство Вспомогательные классы преобразования строк ATL, такие как CA2W, который включает MultiByteToWideChar() вызов и выделение памяти в классе RAII; например.:

#include <atlconv.h> // ATL String Conversion Helpers
// 'test' is a Unicode UTF-16 string.
// Conversion is done from code-page 1250
// (ANSI Central European; Central European (Windows))
CA2W test("ąółź", 1250);

Теперь вы должны иметь возможность использовать строку test в своем Unicode API.

Если у вас нет доступа к ATL или вам нужно решение на основе C ++ STL, вы можете рассмотреть следующий код:

///////////////////////////////////////////////////////////////////////////////
//
// Modern STL-based C++ wrapper to Win32's MultiByteToWideChar() C API.
//
// (based on http://code.msdn.microsoft.com/windowsdesktop/C-UTF-8-Conversion-Helpers-22c0a664)
//
///////////////////////////////////////////////////////////////////////////////

#include <exception>    // for std::exception
#include <iostream>     // for std::cout
#include <ostream>      // for std::endl
#include <stdexcept>    // for std::runtime_error
#include <string>       // for std::string and std::wstring
#include <Windows.h>    // Win32 Platform SDK

//-----------------------------------------------------------------------------
// Define an exception class for string conversion error.
//-----------------------------------------------------------------------------
class StringConversionException 
    : public std::runtime_error
{
public:
    // Creates exception with error message and error code.
    StringConversionException(const char* message, DWORD error)
        : std::runtime_error(message)
        , m_error(error)
    {}

    // Creates exception with error message and error code.
    StringConversionException(const std::string& message, DWORD error)
        : std::runtime_error(message)
        , m_error(error)
    {}

    // Windows error code.
    DWORD Error() const
    {
        return m_error;
    }

private:
    DWORD m_error;
};

//-----------------------------------------------------------------------------
// Converts an ANSI/MBCS string to Unicode UTF-16.
// Wraps MultiByteToWideChar() using modern C++ and STL.
// Throws a StringConversionException on error.
//-----------------------------------------------------------------------------
std::wstring ConvertToUTF16(const std::string & source, const UINT codePage)
{
    // Fail if an invalid input character is encountered
    static const DWORD conversionFlags = MB_ERR_INVALID_CHARS;

    // Require size for destination string
    const int utf16Length = ::MultiByteToWideChar(
        codePage,           // code page for the conversion
        conversionFlags,    // flags
        source.c_str(),     // source string
        source.length(),    // length (in chars) of source string
        NULL,               // unused - no conversion done in this step
        0                   // request size of destination buffer, in wchar_t's
        );
    if (utf16Length == 0) 
    {
        const DWORD error = ::GetLastError();
        throw StringConversionException(
            "MultiByteToWideChar() failed: Can't get length of destination UTF-16 string.",
            error);
    }

    // Allocate room for destination string
    std::wstring utf16Text;
    utf16Text.resize(utf16Length);

    // Convert to Unicode UTF-16
    if ( ! ::MultiByteToWideChar(
        codePage,           // code page for conversion
        0,                  // validation was done in previous call
        source.c_str(),     // source string
        source.length(),    // length (in chars) of source string
        &utf16Text[0],      // destination buffer
        utf16Text.length()  // size of destination buffer, in wchar_t's
        )) 
    {
        const DWORD error = ::GetLastError();
        throw StringConversionException(
            "MultiByteToWideChar() failed: Can't convert to UTF-16 string.",
            error);
    }

    return utf16Text;
}

//-----------------------------------------------------------------------------
// Test.
//-----------------------------------------------------------------------------
int main()
{
    // Error codes
    static const int exitOk = 0;
    static const int exitError = 1;

    try 
    {
        // Test input string:
        //
        // ą - LATIN SMALL LETTER A WITH OGONEK
        std::string inText("x - LATIN SMALL LETTER A WITH OGONEK");
        inText[0] = 0xB9;

        // ANSI Central European; Central European (Windows) code page
        static const UINT codePage = 1250;

        // Convert to Unicode UTF-16
        const std::wstring utf16Text = ConvertToUTF16(inText, codePage);

        // Verify conversion.
        //  ą - LATIN SMALL LETTER A WITH OGONEK
        //  --> Unicode UTF-16 0x0105
        // http://www.fileformat.info/info/unicode/char/105/index.htm
        if (utf16Text[0] != 0x0105) 
        {
            throw std::runtime_error("Wrong conversion.");
        }
        std::cout << "All right." << std::endl;
    }
    catch (const StringConversionException& e)
    {
        std::cerr << "*** ERROR:\n";
        std::cerr << e.what() << "\n";
        std::cerr << "Error code = " << e.Error();
        std::cerr << std::endl;
        return exitError;
    }
    catch (const std::exception& e)
    {
        std::cerr << "*** ERROR:\n";
        std::cerr << e.what();
        std::cerr << std::endl;
        return exitError;
    }
    return exitOk;
}

///////////////////////////////////////////////////////////////////////////////
person Mr.C64    schedule 16.10.2012