2017-01-17 3 views
-3

Итак, я строю язык сценариев, и одна из моих целей - удобные операции с строками. Я пробовал некоторые идеи на C++.C++ Unicode: байты, кодовые точки и графемы

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

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

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

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

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

  • My character type будет не байтом, не графемой, а скорее кодовой точкой. Я назвал его руной, как на языке Go.
  • Строка как серия разложенных рун, что делает индексирование и нарезку 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'. Легко и просто использовать.

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

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

+2

Почему бы не использовать 'char32_t' для хранения "рун"? –

+0

Как его использовать? В прошлый раз, когда я проверил, там не так много информации об этом. –

+2

[char32_t] (http://en.cppreference.com/w/cpp/language/types) "для представления символа UTF-32, который должен быть достаточно большим, чтобы представлять любой кодовый блок UTF-32 (32 бита). Он имеет одинаковый размер, подпись и выравнивание как «std :: uint_least32_t', но является отдельным типом». –

ответ

0

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

Есть, конечно, два пути, вы должны думать о тексте:

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

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

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

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

  • Вы можете (и, вероятно, должны) относиться ко всем std::string «S, как 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

«Вы можете (и, вероятно, должны) обрабатывать все строки' std :: string' как строки с кодировкой UTF-8 ». Существует такая малоизвестная ОС под названием Windows ... –

+1

@ n.m. Я по-прежнему рекомендую использовать строки UTF-8 в качестве носителя данных по умолчанию и обмениваться любыми системными вызовами WinOS с преобразованиями UTF-16 → UTF-8 или UTF-8 → UTF-16. Это наименее подверженное ошибкам решение. – Xirema

+0

Дело в том, что я полагаю, что char32_t реализуется как псевдоним целого. Мой класс - это просто массив, он ведет себя как массив. Если я хочу сохранить специальный символ пробела для последующего сравнения, я могу просто инициализировать его с помощью '' \ xe3 \ x80 \ x80 "', а затем выполнить сравнения. Трюк заключается в преобразовании 'std :: string' в' std :: vector '. Очевидно, необходимо сделать валидацию. Но в большинстве случаев он должен работать нормально. Все, что мне нужно сделать, это получить индексы кодовых точек, а затем разбить строку на 'std :: vector '. Кроме того, он печатает в UTF8! –

Смежные вопросы