utfcpp и широкий API Win32

Хорошо/безопасно/возможно ли использовать крошечную библиотеку utfcpp для преобразования всего, что я получаю из широкой Windows API (FindFirstFileW и т. д.) для действительного представления UTF8 с использованием utf16to8?

Я хотел бы использовать UTF8 для внутреннего использования, но у меня возникли проблемы с получением правильного вывода (через wcout после другого преобразования или обычный cout). Обычные символы ASCII, конечно, работают, но ñä путается.

Или есть более легкая альтернатива?

Спасибо!

ОБНОВЛЕНИЕ: благодаря Гансу (ниже) теперь у меня есть простое преобразование UTF8‹->UTF16 через Windows API. Двустороннее преобразование работает, но в строке UTF8 из UTF16 есть несколько дополнительных символов, которые позже могут вызвать у меня некоторые проблемы...). Поделюсь здесь из чистого дружелюбия :) ):

// UTF16 -> UTF8 conversion
std::string toUTF8( const std::wstring &input )
{
    // get length
    int length = WideCharToMultiByte( CP_UTF8, NULL,
                                      input.c_str(), input.size(),
                                      NULL, 0,
                                      NULL, NULL );
    if( !(length > 0) )
        return std::string();
    else
    {
        std::string result;
        result.resize( length );

        if( WideCharToMultiByte( CP_UTF8, NULL,
                                 input.c_str(), input.size(),
                                 &result[0], result.size(),
                                 NULL, NULL ) > 0 )
            return result;
        else
            throw std::runtime_error( "Failure to execute toUTF8: conversion failed." );
    }
}
// UTF8 -> UTF16 conversion
std::wstring toUTF16( const std::string &input )
{
    // get length
    int length = MultiByteToWideChar( CP_UTF8, NULL,
                                      input.c_str(), input.size(),
                                      NULL, 0 );
    if( !(length > 0) )
        return std::wstring();
    else
    {
        std::wstring result;
        result.resize( length );

        if( MultiByteToWideChar(CP_UTF8, NULL,
                                input.c_str(), input.size(),
                                &result[0], result.size()) > 0 )
            return result;
        else
            throw std::runtime_error( "Failure to execute toUTF16: conversion failed." );
    }
}

person rubenvb    schedule 25.07.2010    source источник


Ответы (2)


В Win32 API уже есть функция для этого, WideCharToMultiByte() с CodePage = CP_UTF8. Избавляет вас от необходимости полагаться на другую библиотеку.

Обычно вы не можете использовать результат с wcout. Его вывод идет на консоль, он использует 8-битную кодировку OEM по устаревшим причинам. Вы можете изменить кодовую страницу с помощью SetConsoleCP(), 65001 — это кодовая страница для UTF-8 (CP_UTF8).

Следующим камнем преткновения будет шрифт, который используется для консоли. Вам придется изменить его, но найти шрифт с фиксированным шагом и полным набором глифов для покрытия Unicode будет сложно. Вы увидите, что у вас есть проблема со шрифтом, когда вы получите квадратные прямоугольники на выходе. Вопросительные знаки — это проблемы с кодировкой.

person Hans Passant    schedule 25.07.2010
comment
Просто для уточнения: шрифт (по крайней мере шрифт TT) позволяет вам указать, какой глиф будет отображаться для кодовой точки, для которой шрифт не содержит глифа. Это обычно пустой прямоугольник, но по сути это может быть что угодно, что выберет дизайнер шрифта. - person Jerry Coffin; 25.07.2010
comment
Я думал, что они доступны, но я не знал, что они предназначены для преобразования UTF-8 → UTF-16 (я по глупости думал, что вместо этого используется кодировка UCS-2). Консольный вывод - действительно грязная вещь. Возможно, я могу вывести UTF-8 в файл и открыть его, скажем, в Notepad++ (это только для проверки того, что делает моя программа)? - person rubenvb; 26.07.2010
comment
Конечно, должно работать. Пока вы можете убедить его, что это файл UTF-8, обычно требуется спецификация в начале файла. Сначала напишите 0xef 0xbb 0xbf, чтобы быть уверенным. - person Hans Passant; 26.07.2010

Почему вы хотите использовать UTF8 внутри? Вы работаете с таким большим количеством текста, что использование UTF16 создаст необоснованные требования к памяти? Даже если это так, вам, вероятно, все равно лучше использовать широкие символы и решать проблемы с памятью каким-либо другим способом (используя дисковый кеш, лучшие алгоритмы или структуры данных).

Ваш код будет намного чище и проще в работе с использованием широких символов, встроенных в API Win32, внутри и с выполнением преобразований UTF8 только при чтении или записи данных, которые требуют этого (например, файлы XML или API REST).

Ваша проблема также может возникнуть в момент, когда вы выводите вывод на консоль, см.: Выводить строки Unicode в консольном приложении Windows

Наконец, я не использовал библиотеку utfcpp, но преобразования UTF8 довольно тривиальны для выполнения с использованием Win32 WideCharToMultiByte и MultiByteToWideChar с CP_UTF8 в качестве кодовой страницы. Лично я бы сделал однократное преобразование и работал с текстом в UTF16, пока не пришло время вывести или передать его в UTF8, если это необходимо.

person Brook Miles    schedule 25.07.2010
comment
Обратите внимание, что широкие символы в Windows являются 16-битными и поэтому должны быть закодированы как UTF-16. Это тоже многобайтовая кодировка. Несмотря на то, что вы, вероятно, с меньшей вероятностью столкнетесь с кодовыми точками Unicode, требующими кодирования двух 16-битных байтов, они существуют, и вы не можете предполагать, что каждое 16-битное значение является отдельным символом. - person sbi; 25.07.2010
comment
Правда, главное преимущество заключается в том, что UTF16 является собственной кодировкой для Windows, и работа с ней означает отсутствие необходимости постоянно конвертировать в какую-либо другую кодировку и обратно при вызове API. - person Brook Miles; 25.07.2010
comment
Я разрабатываю кроссплатформенное приложение, и в Linux wchar_ts в два раза больше, чем в Windows. Все, что мне нужно для Win32 API, — это имена файлов, все остальное — обычный текст (только символы ASCII). Я не вижу смысла обрабатывать вдвое больше байтов, когда достаточно простого std::string. - person rubenvb; 26.07.2010
comment
Причина в том, что а) удвоение количества байтов в этом случае не имеет значения, если только это не огромное количество или вы не используете очень ограниченную платформу, и б) это собственная кодировка ОС, и поэтому ее проще использовать. По сути, я не думаю, что стоит затрачивать дополнительные усилия и сложности на использование UTF8 без каких-либо внешних требований для выполнения su. - person Brook Miles; 30.07.2010
comment
Как я уже сказал, приложение является кроссплатформенным, и мне пришлось бы создать гораздо больший уровень абстракции, если я хочу, чтобы оно работало в любой системе, отличной от Windows. Это либо UTF8, либо UTF16, но один конец все равно придется конвертировать. Я не вникаю в дела чаров. - person rubenvb; 30.07.2010