я использую std::wstring_convert
преобразовать wstring в многобайтовую строку следующим образом:
// convert from wide char to multibyte char
try
{
return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(wideMessage);
}
// thrown by std::wstring_convert.to_bytes() for bad conversions
catch (std::range_error& exception)
{
// do something...
}
Для модульного тестирования блока я прокомментировал как do something...
Я хочу передать wstring, который бросит std::range_error
исключение.
Однако я не смог сформулировать такую строку, которая потерпит неудачу при таком преобразовании. Wstring будет использовать UTF16, и я читал о высоких и низких суррогатах. Например, символ UTF16 D800, за которым следует «b», должен быть недействительным. std::wstring(L"\xd800b");
не компилируется по тем же причинам, возможно. Если я создам wstring, как показано ниже, он не выдаст исключение при преобразовании:
std::wstring wideMessage(L" b");
wideMessage[0] = L'\xd800';
// doesn't throw
std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(wideMessage);
Есть ли подходящая строка, которую я могу использовать, чтобы вызвать исключение во время преобразования?
Я пробовал 5.1, 5.2 и 5.3 из эта ссылка. Я использую Visual Studio 2015.
Реализация Microsoft std::codecvt_utf8
похоже, успешно преобразует любую кодовую единицу UTF-16 в UTF-8, включая суррогатные пары. Это ошибка, поскольку суррогаты не кодируются. И libc ++ (LLVM), и libstdc ++ (GCC) будут правильно генерировать std::range_error
и не в состоянии преобразовать непарных суррогатов.
Глядя на их код, кажется, что единственный способ бросить это, если символ больше, чем Maxcode
параметр шаблона фасета. Например:
std::wstring_convert<std::codecvt_utf8<wchar_t, 0x1>>
Как указано 一 二三 (принятый ответ) Реализация Microsoft codecvt_utf8
кажется, прослушивается.
Я знаю, что строки, с которыми я имею дело, всегда UTF16, и я хочу преобразовать в UTF8. Я закончил тем, что изменил реализацию следующим образом:
// convert from wide char to multibyte char
try
{
return std::wstring_convert<std::codecvt_utf8_utf16 <wchar_t>>().to_bytes(wideMessage);
}
// thrown by std::wstring_convert.to_bytes() for bad conversions
catch (const std::range_error & exception)
{
// do something...
}
Следующее теперь будет бросать правильно:
std::wstring wideMessage(L" b");
wideMessage[0] = L'\xd800';
// throw std::range_error
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(wideMessage);
Я бы никогда не нашел эту ошибку без юнит-тестирования!