Я не думаю, что вы можете сделать это напрямую с помощью codecvt_utf8
или любых других стандартных компонентов библиотеки. Чтобы использовать codecvt_utf8
, вам нужно будет скопировать байты из потока итератора в буфер и преобразовать буфер.
Что-то, как это должно работать:
char32_t read_code_point(iterator& it, const iterator& end)
{
char32_t result;
char32_t* resend = &result + 1;
char32_t* resnext = &result;
char buf[7]; // room for 3-byte UTF-8 BOM and a 4-byte UTF-8 character
char* bufpos = buf;
const char* const bufend = std::end(buf);
std::codecvt_utf8<char32_t> cvt;
while (bufpos != bufend && it != end)
{
*bufpos++ = *it++;
std::mbstate_t st{};
const char* be = bufpos;
const char* bn = buf;
auto conv = cvt.in(st, buf, be, bn, &result, resend, resnext);
if (conv == std::codecvt_base::error)
throw std::runtime_error("Invalid UTF-8 sequence");
if (conv == std::codecvt_base::ok && bn == be)
return result;
// otherwise read another byte and try again
}
if (it == end)
throw std::runtime_error("Incomplete UTF-8 sequence");
throw std::runtime_error("No character read from first seven bytes");
}
Это, кажется, делать больше работы, чем это необходимо, повторно сканировать всю последовательность UTF-8 в [buf, bufpos)
на каждой итерации (и делает вызов виртуальной функции в codecvt_utf8::do_in
) , Теоретически реализация codecvt_utf8::in
может считывать неполную многобайтовую последовательность и сохранять информацию о состоянии в аргументе mbstate_t
, так что следующий вызов возобновится с того места, где последний остался, только потребляя новые байты, а не перерабатывая неполную многобайтную последовательность, которая была уже видел.
Однако реализации не требуется использовать mbstate_t
аргумент для сохранения состояния между вызовами и на практике, по крайней мере одной реализации codecvt_utf8::in
(тот, который я написал для GCC) не использовать его вообще. Из моих экспериментов кажется, что реализация libC++ также не использует его. Это означает, что они прекращают преобразование перед неполной многобайтовой последовательностью и оставляют указатель from_next
(здесь аргумент bn
), указывающий на начало этой неполной последовательности, так что следующий вызов должен начинаться с этой позиции и (надеюсь) предоставить достаточные дополнительные байты для завершения последовательности и разрешить чтение и преобразование полного символа Юникода в char32_t
. Поскольку вы только пытаетесь прочитать один код, это означает, что он вообще не конвертирует, потому что остановка перед неполной многобайтовой последовательностью означает остановку в первом байте.
Вполне возможно, что некоторые реализации сделать использовать mbstate_t
аргумент, так что вы можете изменить функцию выше, чтобы справиться с этим делом, как хорошо, но, чтобы быть портативным он все равно нужно, чтобы справиться с реализациями, которые игнорируют mbstate_t
. Поддержка обоих типов реализации значительно усложнила бы эту функцию, поэтому я оставил ее простой и написал форму, которая должна работать со всеми реализациями, даже если они действительно используют mbstate_t
. Поскольку вы только собираетесь считывать до 7 байт за раз (в худшем случае ... средний случай может быть только одним или двумя байтами, в зависимости от входного текста), стоимость повторного сканирования первых нескольких байтов каждый раз не должно быть огромным.
Чтобы получить лучшую производительность от codecvt_utf8
, вам следует избегать одновременного преобразования одного кода, поскольку он предназначен для преобразования массивов символов, а не отдельных. Так как вам всегда нужно копировать буфер char
, вы можете скопировать большие куски из последовательности ввода итератора и преобразовать целые куски.Это уменьшило бы вероятность обнаружения неполных многобайтовых последовательностей, поскольку только последние 1-3 байта в конце фрагмента нужно было бы переработать, если фрагмент закончится в неполной последовательности, все предыдущее в куске было бы преобразовано ,
Чтобы получить лучшую производительность при чтении отдельных кодовых точек, вы должны, вероятно, полностью избегать codecvt_utf8
и либо свернуть свой собственный (если вам нужно только UTF-8 для UTF-32BE, это не так сложно) или использовать стороннюю библиотеку, такую как ICU.
Не используйте тег [tag: stl], если вы действительно не имеете в виду STL, то есть библиотеку с 1990-х годов, из которой возникли большие части стандартной библиотеки C++. –
О, я вижу. Спасибо за подсказку. –
@JonathanWakely Общее использование, тег wiki, короткая форма тега и полезность термина (кто, собственно, говорит об оригинальной STL больше?) Вроде не согласен с вами там. Я имею в виду, что быть педантичным - это весело, но SO - это нечто большее, чем забава. – Yakk