Вам, вероятно, следует воспользоваться советом Омри и заглянуть в специализированную библиотеку для этого.Тем не менее, если вы просто хотите понять алгоритм для этого, я опубликую его ниже.
По сути, вы можете преобразовать вашу строку в формат с более широкими элементами, например wchar_t
.Обратите внимание, что wchar_t
имеет несколько проблем с переносимостью, потому что wchar_t
может быть разного размера в зависимости от вашей платформы.В Windows wchar_t
составляет 2 байта и поэтому идеально подходит для представления UTF-16.Но в UNIX / Linux это четыре байта и поэтому используется для представления UTF-32.Следовательно, для Windows это будет работать только в том случае, если вы не включили какие-либо кодовые точки Unicode выше 0xFFFF.Для Linux вы можете включить весь диапазон кодовых точек в wchar_t
.(К счастью, эта проблема будет устранена с помощью типов символов C ++ 0x Unicode.)
С учетом этого предостережения вы можете создать функцию преобразования, используя следующий алгоритм:
template <class OutputIterator>
inline OutputIterator convert(const unsigned char* it, const unsigned char* end, OutputIterator out)
{
while (it != end)
{
if (*it < 192) *out++ = *it++; // single byte character
else if (*it < 224 && it + 1 < end && *(it+1) > 127) {
// double byte character
*out++ = ((*it & 0x1F) << 6) | (*(it+1) & 0x3F);
it += 2;
}
else if (*it < 240 && it + 2 < end && *(it+1) > 127 && *(it+2) > 127) {
// triple byte character
*out++ = ((*it & 0x0F) << 12) | ((*(it+1) & 0x3F) << 6) | (*(it+2) & 0x3F);
it += 3;
}
else if (*it < 248 && it + 3 < end && *(it+1) > 127 && *(it+2) > 127 && *(it+3) > 127) {
// 4-byte character
*out++ = ((*it & 0x07) << 18) | ((*(it+1) & 0x3F) << 12) |
((*(it+2) & 0x3F) << 6) | (*(it+3) & 0x3F);
it += 4;
}
else ++it; // Invalid byte sequence (throw an exception here if you want)
}
return out;
}
int main()
{
std::string s = "\u00EAtre";
cout << s.length() << endl;
std::wstring output;
convert(reinterpret_cast<const unsigned char*> (s.c_str()),
reinterpret_cast<const unsigned char*>(s.c_str()) + s.length(), std::back_inserter(output));
cout << output.length() << endl; // Actual length
}
Алгоритм не является полностью универсальным, потому что InputIterator должен быть беззнаковым символом, так что вы можете интерпретировать каждый байт как значение от 0 до 0xFF.OutputIterator является универсальным (просто для того, чтобы вы могли использовать std :: back_inserter и не беспокоиться о распределении памяти), но его использование в качестве универсального параметра ограничено: в основном, он должен выводить в массив элементов, достаточно большой, чтобы представлятьСимволы UTF-16 или UTF-32, такие как wchar_t
, uint32_t
или типы C ++ 0x char32_t
.Кроме того, я не включил код для преобразования последовательностей байтов символов, превышающих 4 байта, но вы должны понять, как работает алгоритм из того, что было опубликовано.
Кроме того, если вы просто хотите считать количество символов, вместо вывода в новый буфер широких символов, вы можете изменить алгоритм, включив в него счетчик, а не OutputIterator.Или еще лучше, просто используйте ответ Марсело Кантоса для подсчета первых байтов.