Зацикливание строки Юникода как символа - PullRequest
0 голосов
/ 19 октября 2019

При следующей строке размер выводится неверно. Почему, и как я могу это исправить?

string str = " ██████";
cout << str.size();
// outputs 19 rather than 7

Я пытаюсь перебрать str символ за символом, чтобы я мог прочитать это в vector<string>, который должен иметь размер7, но я не могу этого сделать, так как вышеприведенный код выводит 19.

Ответы [ 2 ]

4 голосов
/ 19 октября 2019

TL; DR

Элементы size() и length() basic_string возвращают размер в единицах базовой строки, не количество видимых символов . Чтобы получить ожидаемое число:

  • Используйте UTF-16 с префиксом u для очень простых строк, которые не содержат не-BMP, не объединяют символы и не объединяют символы
  • Использование UTF-32 с префиксом U для очень простых строк, которые не содержат символов объединения или объединения
  • Нормализация строки и подсчет для произвольных строк Юникода

" ██████" - это пробел, за которым следует серия из 6 U + 2588 символов. Ваш компилятор использует UTF-8 для std::string. UTF-8 - это кодировка переменной длины , и многие буквы кодируются с использованием нескольких байтов (поскольку, очевидно, вы не можете кодировать более 256 символов одним байтом). В UTF-8 кодовые точки между U + 0800 и U + FFFF кодируются 3 байтами. Поэтому длина строки в UTF-8 составляет 1 + 6 * 3 = 19 байт.

Вы можете проверить с любым конвертером Unicode, например , этот иобратите внимание, что строка кодируется как 20 E2 96 88 E2 96 88 E2 96 88 E2 96 88 E2 96 88 E2 96 88 в UTF-8, и вы также можете перебирать каждый байт вашей строки, чтобы проверить

Если вы хотите, чтобы общее количество видимых символы в строке, тогда это намного сложнее, и решение churill не работает . Прочитайте пример в Twitter

Если вы используете что-либо, кроме самых простых букв, цифр и знаков препинания, ситуация становится более запутанной. В то время как многие люди используют многобайтовые символы кандзи для иллюстрации этих проблем, Twitter обнаружил, что акцентированные гласные вызывают наибольшую путаницу, потому что носители английского языка просто ожидают, что они будут работать. Возьмем следующий пример: слово «кафе». Оказывается, есть две последовательности байтов, которые выглядят одинаково, но используют разное количество байтов:

café  0x63 0x61 0x66 0xC3 0xA9        Using the “é” character, called the “composed character”.
café  0x63 0x61 0x66 0x65 0xCC 0x81   Using the combining diacritical, which overlaps the “e”

Вам нужна библиотека Unicode, такая как ICU до нормализуйте строку и количество. Например, Twitter использует Форма нормализации C

Редактировать:

Поскольку вас интересуют только символы рисования прямоугольников, которые, кажется, не лежат вне BMP и нене содержит никаких комбинирующих символов, UTF-16 и UTF-32 будут работать. Как и std::string, std::wstring также является basic_string и не имеет обязательной кодировки. В большинстве реализаций это часто UTF-16 (Windows) или UTF-16 (* nix), поэтому вы можете использовать его, но это ненадежно и зависит от кодировки исходного кода. Лучше использовать std::u16string (std::basic_string<char16_t>) и std::u32string (std::basic_string<char32_t>). Они будут работать независимо от системы и кодировки исходного файла

std::wstring wstr     = L" ██████";
std::u16string u16str = u" ██████";
std::u32string u32str = U" ██████";
std::cout << str.size();    // may work, returns the number of wchar_t characters
std::cout << u16str.size(); // always returns the number of UTF-16 code units
std::cout << u32str.size(); // always returns the number of UTF-32 code units

Если вам интересно, как это сделать для всех символов Юникода, продолжайте читать ниже

Упомянутая выше проблема «кафе» поднимает вопрос о том, как считать персонажей в строке твит «кафе». Для человеческого глаза длина составляет четыре символа. В зависимости от того, как представлены данные, это может быть пять или шесть байтов UTF-8. Twitter не хочет наказывать пользователя за то, что мы используем UTF-8 или за то, что рассматриваемый клиент API использовал более длинное представление. Таким образом, Twitter считает «кафе» четырьмя символами, независимо от того, какое представление отправлено.

[...]

Твиттер считает длину твита, используя форму нормализации C (NFC) версия текста. Этот тип нормализации способствует использованию полностью комбинированного символа (0xC3 0xA9 из примера кафе) по сравнению с длинной версией (0x65 0xCC 0x81). Twitter также подсчитывает количество кодовых точек в тексте, а не байтов UTF-8. 0xC3 0xA9 из примера кафе - это одна кодовая точка (U + 00E9), которая кодируется как два байта в UTF-8, тогда как 0x65 0xCC 0x81 - это две кодовые точки, закодированные как три байта

Twitter- Подсчет символов

См. Также

2 голосов
/ 19 октября 2019

std::string содержит только 1-байтовые символы (обычно 8-битные, содержат символ UTF-8), вам нужно wchar_t и std::wstring для достижения желаемого:

std::wstring str = L" ██████";
std::cout << str.size();

Несмотря на этопечатает 7 (один пробел и 6 символов Юникода). Обратите внимание на символ L перед строковым литералом, поэтому он будет интерпретирован как широкая строка.

...