Как правильно использовать std :: string на UTF-8 в C ++? - PullRequest
0 голосов
/ 18 мая 2018

Моя платформа - Mac и C ++ 11 (или выше).Я новичок в C ++ и работаю над личным проектом, который обрабатывает китайский и английский языки.UTF-8 является предпочтительной кодировкой для этого проекта.

Я читал некоторые сообщения о переполнении стека, и многие из них предлагают использовать std::string при работе с UTF-8 и избегать wchar_t, так как нет char8_t прямо сейчас для UTF-8.

Однако никто из них не говорит о том, как правильно обращаться с такими функциями, как str[i], std::string::size(), std::string::find_first_of() или std::regex, поскольку эти функции обычно возвращают неожиданные результатыперед лицом UTF-8.

Должен ли я продолжить с std::string или переключиться на std::wstring?Если я останусь с std::string, как лучше справиться с вышеуказанными проблемами?

Ответы [ 4 ]

0 голосов
/ 04 апреля 2019

Подумайте об обновлении до C ++ 20 и std::u8string, это лучшее, что у нас есть на 2019 год для проведения UTF-8.Нет стандартных библиотечных средств для доступа к отдельным кодам или кластерам графем, но, по крайней мере, ваш тип достаточно силен, чтобы хотя бы сказать, что это правда UTF-8.

0 голосов
/ 18 мая 2018

И std::string, и std::wstring должны использовать кодировку UTF для представления Unicode.В частности, в macOS std::string - это UTF-8 (8-битные кодовые единицы), а std::wstring - это UTF-32 (32-битные кодовые единицы);обратите внимание, что размер wchar_t зависит от платформы.

Для обоих size отслеживает количество кодовых единиц вместо количества кодовых точек или кластеров графем.(Кодовая точка - это единица с именем Unicode, одна или несколько из которых образуют кластер графем. Кластеры графем - это видимые символы, с которыми взаимодействуют пользователи, например буквы или эмодзи.)

Хотя я не знаком св представлении Unicode для китайского языка очень возможно, что при использовании UTF-32 количество единиц кода часто очень близко к числу кластеров графем.Очевидно, однако, что это происходит за счет использования в 4 раза больше памяти.

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

Наконец, строки UTF на человеческих языках, которые не используют комбинирование символов, обычно очень хорошо работают с find / regex.Я не уверен насчет китайского, но английский - один из них.

0 голосов
/ 18 мая 2018

Глоссарий Unicode

Unicode - обширная и сложная тема.Я не хочу вдаваться в подробности, но необходим краткий глоссарий:

  1. Кодовые точки : кодовые точки являются основными строительными блоками Unicode, кодовая точка простоцелое число, сопоставленное с , означающим .Целочисленная часть умещается в 32 бита (ну, на самом деле, 24 бита), и значение может быть буквой, диакритическим знаком, пробелом, знаком, смайликом, половиной флага ... и даже может бытьследующая часть читается справа налево ".
  2. Кластеры графем : Кластеры графем - это группы семантически связанных кодовых точек, например, флаг в юникоде представлен связыванием двух кодовых точек;каждый из этих двух, в отдельности, не имеет смысла, но связан вместе в кластере графем, они представляют флаг.Кластеры графемы также используются для сопряжения букв с диакритическими знаками в некоторых сценариях.

Это основа Unicode.Различие между Code Point и Grapheme Cluster может быть в основном скрыто, потому что для большинства современных языков каждый «символ» сопоставляется с одной Code Point (существуют специальные акцентированные формы для часто используемых комбинаций буква + диакритический знак).Тем не менее, если вы решитесь на смайлики, флаги и т. Д., То вам, возможно, придется обратить внимание на это различие.


UTF Primer

Затем серия кодовых точек Unicodeдолжен быть закодирован;Общими кодировками являются UTF-8, UTF-16 и UTF-32, последние две существуют в формах Little-Endian и Big-Endian, всего 5 общих кодировок.

В UTF-X,X - это размер в битах кодовой единицы , каждая кодовая точка представлена ​​в виде одной или нескольких кодовых единиц в зависимости от величины:

  • UTF-8: от 1 до 4Единицы кода,
  • UTF-16: 1 или 2 единицы кода,
  • UTF-32: 1 единица кода.

std::string и std::wstring.

  1. Не используйте std::wstring, если вам нужна переносимость (wchar_t - только 16 бит в Windows);используйте вместо этого std::u32string (он же std::basic_string<char32_t>).
  2. Представление в памяти (std::string или std::wstring) не зависит от представления на диске (UTF-8, UTF-16 или UTF-32), поэтому подготовьтесь к необходимости преобразования на границе (чтение и запись).
  3. Хотя 32-битный wchar_t гарантирует, что единица кода представляет собой полную кодовую точку, она все равно не представляетполный Grapheme Cluster.

Если вы только читаете или сочиняете строки, у вас не должно быть мелких проблем с std::string или std::wstring.

Проблемы начинаются при запускенарезая кубиками, вы должны обратить внимание на (1) границы кодовой точки (в UTF-8 или UTF-16) и (2) границы графемных кластеров.Первый может быть обработан достаточно легко самостоятельно, последний требует использования библиотеки, поддерживающей Unicode.


Выбор std::string или std::u32string?

Если производительность является проблемой,вполне вероятно, что std::string будет работать лучше из-за меньшего объема памяти;хотя интенсивное использование китайского языка может изменить сделку.Как всегда, профиль.

Если кластеры Grapheme не являются проблемой, то std::u32string имеет преимущество в упрощении вещей: 1 единица кода -> 1 точка кода означает, что вы не можете случайно разделить точки кода и всефункции std::basic_string работают "из коробки".

Если вы взаимодействуете с программным обеспечением, принимающим std::string или char* / char const*, тогда придерживайтесь std::string, чтобы избежать обратного преобразования.В противном случае это будет боль.


UTF-8 в std::string.

UTF-8 на самом деле довольно хорошо работает в std::string.

Большинствооперации выполняются «из коробки», потому что кодировка UTF-8 является самосинхронизирующейся и обратно совместимой с ASCII.

Из-за способа кодирования кодовых точек поиск кодовой точки не может случайно совпадать с серединой другой кодовой точки:

  • str.find('\n') работает,
  • str.find("...") работает для сопоставления байта за байтом 1 ,
  • str.find_first_of("\r\n") работает при поиске символов ASCII .

Аналогично, regex в основном работает из коробки.Поскольку последовательность символов ("haha") - это просто последовательность байтов ("哈"), базовые шаблоны поиска должны работать из коробки.

Однако, с осторожностью относитесь к классам символов (таким как [:alphanum:]), поскольку в зависимости от вида и реализации регулярного выражения оно может совпадать или не совпадать с символами Юникода.

Аналогично, с осторожностью относитесь к применению повторителей к не-ASCII "символам", "哈?" может учитывать только последниенеобязательный байт;используйте скобки, чтобы четко очертить повторяющуюся последовательность байтов в таких случаях: "(哈)?".

1 Ключевыми понятиями для поиска являются нормализация и сопоставление;это влияет на все операции сравнения.std::string всегда будет сравнивать (и, следовательно, сортировать) байты за байтом, без учета правил сравнения, специфичных для языка или использования.Если вам нужна полная нормализация / сопоставление, вам нужна полная библиотека Unicode, такая как ICU.

0 голосов
/ 18 мая 2018

std::string и друзья не зависят от кодировки.Единственная разница между std::wstring и std::string заключается в том, что std::wstring использует wchar_t в качестве отдельного элемента, а не char.Для большинства компиляторов последний является 8-битным.Первый должен быть достаточно большим, чтобы содержать любой символ Юникода, но на практике в некоторых системах это не так (например, компилятор Microsoft использует 16-битный тип).Вы не можете хранить UTF-8 в std::wstring;это не то, для чего он предназначен.Он разработан, чтобы быть эквивалентом UTF-32 - строки, в которой каждый элемент представляет собой одну кодовую точку Unicode.

Если вы хотите индексировать строки UTF-8 по кодовой точке Unicode или составному символу Unicode (или некоторому другому), посчитайте длину строки UTF-8 в кодовых точках Unicode или в каком-либо другом объекте Unicode или найдите по кодовой точке Unicode, вам нужно будет использовать что-то, кроме стандартной библиотеки. ICU - одна из библиотек на местах;могут быть и другие.

Вероятно, стоит отметить, что, если вы ищете символы ASCII, вы можете обрабатывать байтовый поток UTF-8, как если бы он был байтовым.Каждый символ ASCII кодирует в UTF-8 то же самое, что и в ASCII, и гарантируется, что каждый многобайтовый модуль в UTF-8 не будет включать никаких байтов в диапазон ASCII.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...