кодовая точка — C ++ Unicode: байты, кодовые точки и графемы

Итак, я создаю язык сценариев, и одна из моих целей — это удобные строковые операции. Я попробовал некоторые идеи в C ++.

  • Строка как последовательность байтов и свободных функций, которые возвращают векторы, содержащие индексы кодовых точек.
  • Класс-обертка, который объединяет строку и вектор, содержащий индексы.

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

В итоге я создал класс-оболочку вокруг массива char, равного 4 байтам: строка, в памяти которой ровно 4 байта, не больше и не меньше.

Создав этот класс, я испытал желание просто обернуть его в std::vector из этого в другом классе и построить оттуда, таким образом, делая строковый тип кодовых точек. Я не знаю, хороший ли это подход, в конечном итоге он будет намного удобнее, но в итоге будет тратить больше места.

Итак, перед публикацией некоторого кода приведу более организованный список идей.

  • Мой символьный тип был бы не байтом или графемой, а скорее кодовой точкой. Я назвал ее руной, подобной той, что на языке го.
  • Строка как серия разложенных рун, что делает индексацию и нарезку O1.
  • Поскольку руна теперь является классом, а не примитивом, она может быть расширена с помощью методов обнаружения пробела в Юникоде: mysring[0].is_whitespace()
  • Я до сих пор не знаю, как обращаться с графемами.

Любопытный факт! При создании прототипа класса рун странным было то, что он всегда печатается в UTF8. Поскольку моя руна — это не int32, а 4-байтовая строка, в результате получаются некоторые интересные свойства.

Мой код:

class rune {
char data[4] {};
public:
rune(char c) {
data[0] = c;
}

// This constructor needs a string, a position and an offset!
rune(std::string const & s, size_t p, size_t n) {
for (size_t i = 0; i < n; ++i) {
data[i] = s[p + i];
}
}

void swap(rune & other) {
rune t = *this;
*this = other;
other = t;
}

// Output as UTF8!
friend std::ostream & operator <<(std::ostream & output, rune input) {
for (size_t i = 0; i < 4; ++i) {
if (input.data[i] == '\0') {
return output;
}
output << input.data[i];
}
return output;
}
};

Идеи обработки ошибок:

Я не люблю использовать исключения в C ++. Моя идея заключается в том, что, если конструктор не удается, инициализировать руну как 4 '\0', а затем явно перегрузите оператор bool, чтобы вернуть false, если первый байт цикла был '\0', Легко и просто использовать.

Итак, мысли? Мнения? Разные подходы?

Даже если моя руническая строка слишком большая, по крайней мере, у меня есть рунический тип. Маленький и быстрый для копирования. 🙂

-3

Решение

Похоже, вы пытаетесь изобрести велосипед.

Есть, конечно, два способа думать о тексте:

  • Как массив кодовых точек
  • Как закодированный массив байтов.

В некоторых кодовых базах эти два представления одинаковы (а все кодировки в основном являются массивами char32_t или же unsigned int). В некоторых (я склонен сказать «большинство», но не цитируйте меня об этом), кодированный массив байтов будет использовать UTF-8, где кодовые точки преобразуются в переменные длины байтов перед помещением в структуру данных ,

И, конечно, многие кодовые базы просто полностью игнорируют Unicode и хранят свои данные в ASCII. Я не рекомендую это.

Для ваших целей, хотя имеет смысл написать класс для «обтекания» ваших данных (хотя я бы не назвал это runeЯ бы просто назвал это codepoint), вы захотите подумать о своей семантике.

  • Вы можете (и, вероятно, должны) лечить все std::stringкак строки в кодировке UTF-8, и предпочитаю это как интерфейс по умолчанию для работы с текстом. Это безопасно для большинства внешних интерфейсов — единственный раз, когда он потерпит неудачу, это когда он взаимодействует со входом UTF-16, и вы можете написать угловые случаи для этого — и это сэкономит вам больше памяти, при этом соблюдая общие строковые соглашения (это лексикографически сопоставимый, который является большим).
  • Если вам нужно работать с вашими данными в форме кодов, вам нужно написать структуру (или класс) с именем codepointсо следующими полезными функциями и конструкторами
    • Хотя мне приходилось писать код, который обрабатывает текст в форме кодов (особенно для средства визуализации шрифтов), это, вероятно, не как вы должны хранить свой текст. Сохранение текста в виде кодовых точек приводит к проблемам позже, когда вы постоянно сравниваете со строками в кодировке UTF-8 или ASCII.

код:

struct codepoint {
char32_t val;
codepoint(char32_t _val = 0) : val(_val) {}
codepoint(std::string const& s);
codepoint(std::string::const_iterator begin, std::string::const_iterator end);
//I don't know the UTF-8→codepoint conversion off-hand. There are lots of places
//online that show how to do this

std::string to_utf8() const;
//Again, look up an algorithm. They're not *too* complicated.
void append_to_string_as_utf8(std::string & s) const;
//This might be more performant if you're trying to reduce how many dynamic memory
//allocations you're making.

//codepoint(std::wstring const& s);
//std::wstring to_utf16() const;
//void append_to_string_as_utf16(std::wstring & s) const;

//Anything else you need, equality operator, comparison operator, etc.
};
0

Другие решения

Других решений пока нет …