Итак, я создаю язык сценариев, и одна из моих целей — это удобные строковые операции. Я попробовал некоторые идеи в C ++.
У обеих идей была проблема, и эта проблема заключалась в том, что я должен вернуть. Это не мог быть символ, и если бы это была строка, это было бы пустым пространством.
В итоге я создал класс-оболочку вокруг массива char, равного 4 байтам: строка, в памяти которой ровно 4 байта, не больше и не меньше.
Создав этот класс, я испытал желание просто обернуть его в std::vector
из этого в другом классе и построить оттуда, таким образом, делая строковый тип кодовых точек. Я не знаю, хороший ли это подход, в конечном итоге он будет намного удобнее, но в итоге будет тратить больше места.
Итак, перед публикацией некоторого кода приведу более организованный список идей.
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'
, Легко и просто использовать.
Итак, мысли? Мнения? Разные подходы?
Даже если моя руническая строка слишком большая, по крайней мере, у меня есть рунический тип. Маленький и быстрый для копирования. 🙂
Похоже, вы пытаетесь изобрести велосипед.
Есть, конечно, два способа думать о тексте:
В некоторых кодовых базах эти два представления одинаковы (а все кодировки в основном являются массивами char32_t
или же unsigned int
). В некоторых (я склонен сказать «большинство», но не цитируйте меня об этом), кодированный массив байтов будет использовать UTF-8, где кодовые точки преобразуются в переменные длины байтов перед помещением в структуру данных ,
И, конечно, многие кодовые базы просто полностью игнорируют Unicode и хранят свои данные в ASCII. Я не рекомендую это.
Для ваших целей, хотя имеет смысл написать класс для «обтекания» ваших данных (хотя я бы не назвал это rune
Я бы просто назвал это codepoint
), вы захотите подумать о своей семантике.
std::string
как строки в кодировке UTF-8, и предпочитаю это как интерфейс по умолчанию для работы с текстом. Это безопасно для большинства внешних интерфейсов — единственный раз, когда он потерпит неудачу, это когда он взаимодействует со входом UTF-16, и вы можете написать угловые случаи для этого — и это сэкономит вам больше памяти, при этом соблюдая общие строковые соглашения (это лексикографически сопоставимый, который является большим).codepoint
со следующими полезными функциями и конструкторами
код:
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.
};
Других решений пока нет …