Unicode — C ++, как получить следующий многобайтовый символ

Это способ получить следующий полный символ в многобайтовой строке, например, «z \ u00df \ u6c34 \ U0001d10b» или «zß 水 ��» будет представлен как 4 символа, исключая нулевое завершение в самой широкой строке, но, возможно, 9 символов в многобайтовой строка. Я использовал приведенный ниже код для преобразования в и из строки, так как я использовал widestirng внутри, но они кажутся тонкими проблемами, если правильная длина не указана для __wideToString, даже если длина больше, чем должна быть. Я также понял, что, вероятно, могу пропустить все преобразования в и из wstring, используя только строку, если я могу просто узнать, сколько символов в многобайтовой строке составляет следующий полный символ. Так, скажем, в строке u8 «u6c34 \ U0001d10b», которая может храниться в 6 символах, я хотел бы только следующие 2, которые будут «水». Кто-нибудь может направить меня в решении этой проблемы?

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

static
std::string __wideToString(const std::wstring & ws){
if(ws.empty()){throw std::invalid_argument("Wide string must have length >= 1");}
std::setlocale(LC_ALL, "");
size_t length = sizeof(wchar_t)*ws.length();
std::string str(length,' ');
if((length=wcstombs(&str[0], ws.c_str(), length))==size_t(-1)){//return -1 on invalid conversion
throw std::length_error("Conversion Error Invalid Wide Character");
}
str.resize(length); // Shrink to fit.
return str;
}

static
std::wstring __stringToWide(const std::string & str){
if(str.empty()){throw std::invalid_argument("String must have length >= 1");}
std::setlocale(LC_ALL, "");
size_t length = str.length();
std::wstring ws(length, L' '); // Overestimate number of code points.
if((length=mbstowcs(&ws[0], str.c_str(), length))==size_t(-1)){//return -1 on invalid conversion
throw std::length_error("Conversion Error Invalid Multibyte Character");
}
ws.resize(length); // Shrink to fit.
return ws;
}

0

Решение

wcstombs() не работает для символов за пределами юникода 0 — 0xff.

Он либо потерпит неудачу с возвращаемым значением -1 (для китайских букв и т. Д.), Либо тихо выдаст неверный вывод (например, удалит диакритические знаки из ‘ā’, чтобы он стал ‘a’).

Проблема в том, что то, что вы делаете, не имеет смысла, если у вас есть символы, которые не могут быть представлены обычной std :: string. Не существует API операционной системы или функций C ++ 03/11, которые поддерживают то, что вы пытаетесь сделать.

Методы с такими именами, как wideToString (), не имеют смысла, если только у вас не ограниченный ANSI-подобный набор символов. stringToWide () будет иметь смысл, хотя.

Возвращаясь к вашему вопросу — Windows хранит полезную нагрузку wstring как UTF-16, и каждый wchar_t внутри нее представляет собой одну 16-битную кодовую единицу UTF-16 (поэтому вам нужно два wchar_ts для символов, кроме unicodes 0xffff). Linux хранит полезную нагрузку wstring как UTF-8, но wchar_t — это 32-битный код UTF-32.

Поэтому в Windows вы можете найти в сети некоторые функции декодирования UTF-16, чтобы узнать, где начинается следующий символ. Но опять же, это не поможет вам.

1

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

Эта функция даст вам длину байта и кодовую точку:

void getNextCharByteLengthAndCodePoint(const char* ch, size_t& byteLength, char32_t& codePoint)
{
unsigned char firstByte(*ch);

//Check against 1000 0000 is the first byte set?
if ((firstByte & BIT_10000000) == 0)
{
// Codepoint is everything 0111 1111
codePoint = firstByte & BIT_01111111;
byteLength = 1;
}
//Check against 1110 0000 making sure we are 1100 0000
else if ((firstByte & BIT_11100000) == BIT_11000000)
{
// Codepoint is everything 0001 1111
codePoint = firstByte & BIT_00011111;
byteLength = 2;
}
//Check against 1111 0000 making sure we are 1110 0000
else if ((firstByte & BIT_11110000) == BIT_11100000)
{
// Codepoint is everything 0000 1111
codePoint = firstByte & BIT_00001111;
byteLength = 3;
}
//Check against 1111 1000 making sure we are 1111 0000
else if ((firstByte & BIT_11111000) == BIT_11110000)
{
// Codepoint is everything 0000 0111
codePoint = firstByte & BIT_00000111;
byteLength = 4;
}
else
{
throw std::runtime_error("Invalid UTF8 encoding");
}

for (int i = 1; i < byteLength; ++i)
{
//Go through the other 'byteLength' bytes and shift everything 6
codePoint = ((codePoint << 6) | (ch[i] & BIT_00111111));
}
}
1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector