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, некоторые простые операторы манипуляции (вставка или стирание), преобразования верхнего и нижнего регистра, независимый от случая поиск и т.д.
Но не ожидайте, что эти функции будут всегда правильными. Например, они действительно не знают о объединении диакритики или о возможных способах кодирования одного и того же текста.