Вот фрагмент кода, который использует std::codecvt_utf8<>
грань для преобразования из wchar_t
до UTF-8. В Visual Studio 2012 мои ожидания не оправдались (см. Условие в конце кода). Мои ожидания неверны? Зачем? Или это проблема библиотеки Visual Studio 2012?
#include <locale>
#include <codecvt>
#include <cstdlib>
int main ()
{
std::mbstate_t state = std::mbstate_t ();
std::locale loc (std::locale (), new std::codecvt_utf8<wchar_t>);
typedef std::codecvt<wchar_t, char, std::mbstate_t> codecvt_type;
codecvt_type const & cvt = std::use_facet<codecvt_type> (loc);
wchar_t ch = L'\u5FC3';
wchar_t const * from_first = &ch;
wchar_t const * from_mid = &ch;
wchar_t const * from_end = from_first + 1;
char out_buf[1];
char * out_first = out_buf;
char * out_mid = out_buf;
char * out_end = out_buf + 1;
std::codecvt_base::result cvt_res
= cvt.out (state, from_first, from_end, from_mid,
out_first, out_end, out_mid);
// This is what I expect:
if (cvt_res == std::codecvt_base::partial
&& out_mid == out_end
&& state != 0)
;
else
abort ();
}
Ожидание здесь заключается в том, что out()
Функция выводит один байт преобразования UTF-8 за раз, но середина if
Условие выше ложно с Visual Studio 2012.
Что терпит неудачу, так это out_mid == out_end
а также state != 0
условия. По сути, я ожидаю, что по крайней мере один байт будет произведен и необходимое состояние, чтобы следующий байт последовательности UTF-8 мог быть воспроизведен, был сохранен в state
переменная.
Стандартное описание partial
код возврата codecvt::do_out
говорит именно это:
в таблице 83:
partial
не все исходные символы преобразованы
В 22.4.1.4.2 [locale.codecvt.virtuals] / 5:
Возвращает: Значение перечисления, приведенное в таблице 83. Возвращаемое значение
partial
, если(from_next==from_end)
, указывает, что либо последовательность назначения
не поглотил все доступные элементы назначения, или что необходимы дополнительные исходные элементы, прежде чем может быть создан другой элемент назначения.
В вашем случае не все (нулевые) исходные символы были преобразованы, что технически ничего не говорит о содержимом выходной последовательности (не указано предложение ‘if’ в предложении), но, вообще говоря, «целевая последовательность не поглощена все доступные элементы назначения »здесь говорит о допустимых многобайтовых символах. Они элементы многобайтовой последовательности символов, созданной codecvt_utf8
,
Было бы неплохо иметь более четкую стандартную формулировку, но вот два косвенных доказательства:
Один: функция преобразования старого в многобайтовый C std::wcsrtombs
(чьи специфичные для локали варианты обычно вызываются существующими реализациями codecvt::do_out
для системных языков) определяется следующим образом:
Преобразование прекращается […], когда следующий многобайтовый символ превысит лимит суммарных байтов, которые будут сохранены в массив, на который указывает dst.
И во-вторых, посмотрите на существующие реализации codecvt_utf8
: вы уже исследовали Microsoft, и вот что в libc ++: codecvt_utf8::do_out
здесь звонки ucs2_to_utf8
на Windows и ucs4_to_utf8
в других системах и ucs2_to_utf8 делает следующее (комментарии мои):
else if (wc < 0x0800)
{
// not relevant
}
else // if (wc <= 0xFFFF)
{
if (to_end-to_nxt < 3)
return codecvt_base::partial; // <- look here
*to_nxt++ = static_cast<uint8_t>(0xE0 | (wc >> 12));
*to_nxt++ = static_cast<uint8_t>(0x80 | ((wc & 0x0FC0) >> 6));
*to_nxt++ = static_cast<uint8_t>(0x80 | (wc & 0x003F));
}
в выходную последовательность ничего не записывается, если она не может вместить многобайтовый символ, полученный в результате использования одного входного широкого символа.
Хотя нет прямой ссылки на это, я думаю, что это наиболее логичное поведение std::codecvt::out
, Рассмотрим следующий сценарий:
std::codecvt::out
так же, как вы — не переводить никаких символов (возможно, не зная) в ваш out_buf
,out_buf
(снова используя std::codecvt::out
) такой, что он добавляет контент, который уже находится внутриbuf_mid
как вы знаете, он указывает сразу после вашей строки, которую вы перевели на первом шаге.std::codecvt::out
работал в соответствии с вашими ожиданиями (buf_mid
указывая на символ после первого) затем первый символ вашего out_buf
никогда не будет написано, что будет не совсем то, что вы хотите / ожидаете в этом случае.По сути, extern_type*& to_next
(последний параметр std::codecvt::out
) здесь для вас как справка о том, где вы оставили — так что вы знаете, где продолжить — что в вашем случае действительно та же позиция, что и с того, с чего вы начали (extern_type* to
) параметр.