Как преобразовать большую строку символов в кодировке UTF-8 в CStringW (UTF-16)?

#html #unicode #utf-8 #mfc

#HTML #юникод #utf-8 #mfc

Вопрос:

У меня проблема с преобразованием строки в кодировке UTF-8 в кодировку UTF-16 CStringW .

Вот мой исходный код:

 CStringW ConvertUTF8ToUTF16( __in const CHAR * pszTextUTF8 )
{
    _wsetlocale( LC_ALL, L"Korean" );
    if ( (pszTextUTF8 == NULL) || (*pszTextUTF8 == '') )
    {
        return L"";
    }
    const size_t cchUTF8Max = INT_MAX - 1;
    size_t cchUTF8;
    HRESULT hr = ::StringCbLengthA( pszTextUTF8, cchUTF8Max, amp;cchUTF8 );
    if ( FAILED( hr ) )
    {
        AtlThrow( hr );
    }
      cchUTF8;
    int cbUTF8 = static_cast<int>( cchUTF8 );

    int cchUTF16 = ::MultiByteToWideChar(
        CP_UTF8,
        MB_ERR_INVALID_CHARS,
        pszTextUTF8,
        -1,
        NULL,
        0
        );

    CString strUTF16;
    strUTF16.GetBufferSetLength(cbUTF8);
    WCHAR * pszUTF16 = new WCHAR[cchUTF16];

    int result = ::MultiByteToWideChar(
        CP_UTF8,
        0,
        pszTextUTF8,
        cbUTF8,
        pszUTF16,
        cchUTF16
        );
    ATLASSERT( result != 0 );
    if ( result == 0 )
    {
        AtlThrowLastWin32();
    }
    strUTF16.Format(_T("%s"), pszUTF16);
    return strUTF16;
}
  

pszTextUTF8 содержимое файла htm в формате UTF-8.
Когда объем htm-файла меньше 500 КБ, этот код работает хорошо.
но при преобразовании htm-файла размером более 500 КБ (например, htm-файла размером 648 КБ, который у меня есть.)
pszUTF16 содержит все содержимое файла, но strUTF16 не является. (примерно половина)
Я думаю, что File open не ошибается.

В strUTF16 m_pszData нем есть все содержимое, как мне это получить? strUTF16.Getbuffer(); не работает.

Комментарии:

1. CStringW strUTF16 = CA2W(pszTextUTF8, CP_UTF8) это все, что вам нужно. Если он все еще не работает, покажите часть, которая читает файл. При чтении файла UTF8 убедитесь, что вы оставили место для nul-terminator и обнулили последний байт.

2. Большое спасибо. Я решил свою проблему. Это была просто проблема эффективности текстового визуализатора MS Visual Studio. Я экспортировал содержимое CString в виде файла, который я преобразовал. Чем файл имеет полное содержимое! Еще раз спасибо ~

3. @BarmakShemirani: Это простое решение, которое работает без особых раздумий. Однако это создает дополнительную копию и требует в два раза больше памяти, чем конечная строка. Поскольку рассматриваемый код хочет создать CStringW объект, вы можете записать его непосредственно в его буфер и сохранить как дополнительный буфер, так и операцию копирования.

Ответ №1:

Код в вопросе полон ошибок, где-то порядка 1 ошибки на 1-2 строки кода.

Вот краткое резюме:

 _wsetlocale( LC_ALL, L"Korean" );
  

Изменение глобального параметра в функции преобразования является неожиданным и приведет к нарушению кода, вызывающего это. Это даже не обязательно; вы не используете языковой стандарт для преобразования кодировки.

 HRESULT hr = ::StringCbLengthA( pszTextUTF8, cchUTF8Max, amp;cchUTF8 );
  

Это передает неправильное cchUTF8Max значение (согласно документации) и подсчитывает количество байтов (в зависимости от количества символов, т. Е. Единиц кода). Помимо всего этого, вам даже не нужно знать количество единиц кода, поскольку вы никогда его не используете (ну, вы используете, но это просто еще одна ошибка).

 int cbUTF8 = static_cast<int>( cchUTF8 );
  

Хотя это исправляет префикс (количество символов в отличие от количества символов), это не спасет вас от его последующего использования для чего-то, что имеет несвязанное значение.

 strUTF16.GetBufferSetLength(cbUTF8);
  

Это изменяет размер объекта string, который в конечном итоге должен содержать символы в кодировке UTF-16. Но он не использует правильное количество символов (предыдущий вызов MultiByteToWideChar предоставил бы это значение), а выбирает совершенно не связанное значение: количество байтов в исходной строке в кодировке UTF-8.

Но на этом дело не заканчивается, эта строка кода также отбрасывает указатель на внутренний буфер, который был готов к записи. Сбой вызова ReleaseBuffer является естественным следствием, поскольку вы решили не читать документацию.

 WCHAR * pszUTF16 = new WCHAR[cchUTF16];
  

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

 strUTF16.Format(_T("%s"), pszUTF16);
  

Вероятно, это анти-шаблон, связанный с printf семейством функций. Это сложный способ записи CopyChars (или Append ).

Теперь, когда это прояснилось, вот правильный способ написания этой функции (или, по крайней мере, один способ сделать это):

 CStringW ConvertUTF8ToUTF16( __in const CHAR * pszTextUTF8 ) {
    // Allocate return value immediately, so that (N)RVO can be applied
    CStringW strUTF16;
    if ( (pszTextUTF8 == NULL) || (*pszTextUTF8 == '') ) {
        return strUTF16;
    }

    // Calculate the required destination buffer size
    int cchUTF16 = ::MultiByteToWideChar( CP_UTF8,
                                          MB_ERR_INVALID_CHARS,
                                          pszTextUTF8,
                                          -1,
                                          nullptr,
                                          0 );

    // Perform error checking
    if ( cchUTF16 == 0 ) {
        throw std::runtime_error( "MultiByteToWideChar failed." );
    }

    // Resize the output string size and use the pointer to the internal buffer
    wchar_t* const pszUTF16 = strUTF16.GetBufferSetLength( cchUTF16 );

    // Perform conversion (return value ignored, since we just checked for success)
    ::MultiByteToWideChar( CP_UTF8,
                           MB_ERR_INVALID_CHARS, // Use identical flags
                           pszTextUTF8,
                           -1,
                           pszUTF16,
                           cchUTF16 );

    // Perform required cleanup
    strUTF16.ReleaseBuffer();

    // Return converted string
    return strUTF16;
}