проблема с фасетом std :: codecvt_utf8

Вот фрагмент кода, который использует 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 переменная.

4

Решение

Стандартное описание 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));
}

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

4

Другие решения

Хотя нет прямой ссылки на это, я думаю, что это наиболее логичное поведение 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) параметр.

2

По вопросам рекламы [email protected]