Utf-8 в С++: быстрые и грязные трюки

Я знаю, что были вопросы о utf-8, в основном о библиотеках, которые могли бы манипулировать объектами типа utf-8 'string'.

Тем не менее, я работаю над "интернационализированным" проектом (веб-сайтом, из которого я кодирую бэкэнд С++... не спрашиваю), где, даже если мы имеем дело с utf-8, нам не нужны такие библиотеки, В большинстве случаев простые методы std::string или алгоритмы STL являются очень достаточными для наших нужд, и на самом деле это цель использования utf-8 в первую очередь.

Итак, я ищу здесь капитализацию "быстрых и грязных" трюков, которые вы знаете о связанных с utf-8, хранящихся как std::string (no const char *, мне все равно c-style code действительно, у меня есть лучшие вещи, чем постоянно беспокоиться о размере моего буфера).

Например, вот "Быстрая и грязная" трюк, чтобы получить количество символов (что полезно знать, будет ли оно соответствовать вашему экрану):

#include <string>
#include <algorithm>

// Let remember than in utf-8 encoding, a character may be
// 1 byte: '0.......'
// 2 bytes: '110.....' '10......'
// 3 bytes: '1110....' '10......' '10......'
// 4 bytes: '11110...' '10......' '10......' '10......'
// Therefore '10......' is not the beginning of a character ;)

const unsigned char mask = 0xC0;
const unsigned char notUtf8Begin = 0x80;

struct Utf8Begin
{
  bool operator(char c) const { return (c & mask) != notUtf8Begin; }
};

// Let count
size_t countUtf8Characters(const std::string& s)
{
  return std::count_if(s.begin(), s.end(), Utf8Begin());
}

На самом деле мне еще предстоит столкнуться с usecase, когда мне понадобится что-то еще, чем количество символов, и что std::string или алгоритмы STL не предлагают бесплатно, поскольку:

  • сортировка работает как ожидалось
  • никакая часть слова не может быть смущена как слово или часть другого слова

Я хотел бы знать, есть ли у вас другие сопоставимые трюки, как для подсчета, так и для других простых задач.
Повторяю, я знаю о ICU и Utf8-CPP, но меня это не интересует, так как мне не нужно полноценное лечение (и на самом деле мне никогда не нужно больше, чем количество персонажей).
Я также повторяю, что я не заинтересован в лечении char *, они старомодны.

Ответ 1

Хорошо, этот грязный трюк не сработает. Во-первых, каково значение маски после этого:

   const unsigned char mask = 0x11000000;
   const unsigned char notUtf8Begin = 0x10000000;

Возможно, вы смешиваете шестнадцатеричное представление с двоичным.

Во-вторых, как вы правильно говорите в кодировке utf-8, символ может иметь длину в несколько байтов. std:: count_if будет перебирать все байты в последовательности UTF8. Но то, что вам действительно нужно, это посмотреть на ведущий байт для каждого персонажа и пропустить остальную часть до следующего символа.

Нетрудно реализовать один цикл, который выполняет расчет и прыгает вперед используя простую таблицу масок для ведущих байтов.

В конце вы получите тот же O (n) для проверки символов, и он будет работать с каждой строкой UTF8.

Ответ 2

Сортировка UTF_8 как двоичного файла не будет сортироваться в порядке "Юникод". BOCU-1 будет. Как уже было сказано, ваш "как ожидалось" является довольно низким баром для неанглийского контента.

Ответ 3

Мы справляемся с этим также в OpenLieroX (это действительно хорошо в игре, я думаю).

У нас есть куча полезных функций/алгоритмов для таких std:: строк UTF-8. См. Unicode.h и Unicode.cpp. Например, существуют итераторы UTF8, некоторые простые операторы манипуляции (вставка или стирание), преобразования верхнего и нижнего регистра, независимый от случая поиск и т.д.

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