Текстовые строки в языках программирования

427f6d9623760203074039638a24b481

Уже давно прошли те времена, когда текстовые строки в языках программирования были исключительно байтовыми без поддержки символов национальных алфавитов, а в некоторых случаях еще и ограничены размером не более 255 символов. В настоящее время наоборот, сложно найти такой язык программирования, который НЕ «поддерживает» юникод в текстовых строках.

Если вы обратили внимание, то слово «поддерживает» взято в кавычки и как говорил Винипух, это жжж не спроста, ведь с появлением Unicode понятие «символ» в текстовых строках стало не совсем однозначным.

Есть старая статья о проблемах поддержки Unicode в разных языках программирования: The importance of language-level abstract Unicode strings Matt Giuca

Основной смысл которой сводится к тому, чтобы призвать разработчиков языков программирования абстрагироваться от схем кодирования Unicode (доступом к отдельным байтам), и оставить для программистов только возможность работы с последовательностью символов, чтобы предотвратить большинство ошибок Unicode, так как с приходом эры Unicode изменилось само понятие символа и текстовой строки!

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

Языки следующего поколения должны предоставлять только строковые операции, ориентированные на символы (кроме случаев, когда программист явно запрашивает кодировку текста). Тогда остальные из нас смогут вернуться к программированию, вместо того, чтобы беспокоиться о проблемах кодирования (строк в Unicode).

Терминология

code point — это примерно то же, что мы привыкли называть символом. Но не совсем. Например, буква «ё» может быть как одним code point’ом, так и двумя — буквой «е» и символом «две точки над предыдущей буквой».

code unit — это единицы кодировки (utf-8, utf-16 или utf-32)

Соответственно с приходом Unicode появились и следующие проблемы:

  • У текстовых строк могут быть разные кодовой единицы (UTF-8: кодовая единица = 8 бит или 1 байт, UTF-16: кодовая единица = 16 бит или 2 байта. UTF-32: кодовая единица = 32 бита или 4 байта)

  • Имеем разное количеством байт на один code point

  • Некоторые символы можно закодировать разным количеством code point, например, е + ̈ == ̈ё. Это увеличивает размер данных, но добавляет только один символ.

  • Проблемы с индексацией строк (по байтно или по символьно). Доступ к элементу символьной строки Unicode стал не O (1) как у массива, а O (n) так как приходится сканировать строку для подсчета количества Unicode символов.

  • Требуется проверка корректности данных строки при сериализации/десериализации (контроль ошибок преобразования кодировок / валидности кодовых точек)

И это только самые основные проблемы при использовании Unicode! А есть еще группы символов, символы, которые не символы, поиск и сортировка или например, модификаторы

Модификаторы

Объединитель нулевой ширины (ZWJ) является непечатным символом в компьютерном наборе некоторых сложных шрифтов, таких как арабский или любой индийский шрифт. При помещении между двумя символами, которые в противном случае не были бы связаны, ZWJ заставляет их печататься в объединённой форме.

Разъединитель нулевой ширины (ZWNJ) — это непечатный символ в компьютерных наборах письменностей с лигатурами. При размещении между двумя символами, которые в противном случае были бы соединены в лигатуру, ZWNJ заставляет их печататься в их окончательной и первоначальной формах, соответственно. Действует как пробел, но используется в том случае, когда желательно удерживать слова рядом друг с другом или соединить слово с его морфемой.

Однако подавляющее большинство языков программирования может оперировать символьными строками как байтовыми массивами. А так как способов кодирования Unicode символов, а соответственно и типов литералов для таких строк существует великое множество, поэтому сложилась довольно широко распространена практика использовать у текстовых строк-литералов различные модификаторы для разных форматов кодирования.

Вот примеры определения разных типов строк в С++

    // Character literals
    auto c0 =   'A'; // char
    auto c1 = u8'A'; // char
    auto c2 =  L'A'; // wchar_t
    auto c3 =  u'A'; // char16_t
    auto c4 =  U'A'; // char32_t

    // Multicharacter literals
    auto m0 = 'abcd'; // int, value 0x61626364

    // String literals
    auto s0 =   "hello"; // const char*
    auto s1 = u8"hello"; // const char* before C++20, encoded as UTF-8,
                         // const char8_t* in C++20
    auto s2 =  L"hello"; // const wchar_t*
    auto s3 =  u"hello"; // const char16_t*, encoded as UTF-16
    auto s4 =  U"hello"; // const char32_t*, encoded as UTF-32

    // Raw string literals containing unescaped \ and "
    auto R0 =   R"("Hello \ world")"; // const char*
    auto R1 = u8R"("Hello \ world")"; // const char* before C++20, encoded as UTF-8,
                                      // const char8_t* in C++20
    auto R2 =  LR"("Hello \ world")"; // const wchar_t*
    auto R3 =  uR"("Hello \ world")"; // const char16_t*, encoded as UTF-16
    auto R4 =  UR"("Hello \ world")"; // const char32_t*, encoded as UTF-32

    // Combining string literals with standard s-suffix
    auto S0 =   "hello"s; // std::string
    auto S1 = u8"hello"s; // std::string before C++20, std::u8string in C++20
    auto S2 =  L"hello"s; // std::wstring
    auto S3 =  u"hello"s; // std::u16string
    auto S4 =  U"hello"s; // std::u32string

    // Combining raw string literals with standard s-suffix
    auto S5 =   R"("Hello \ world")"s; // std::string from a raw const char*
    auto S6 = u8R"("Hello \ world")"s; // std::string from a raw const char* before C++20, encoded as UTF-8,
                                       // std::u8string in C++20
    auto S7 =  LR"("Hello \ world")"s; // std::wstring from a raw const wchar_t*
    auto S8 =  uR"("Hello \ world")"s; // std::u16string from a raw const char16_t*, encoded as UTF-16
    auto S9 =  UR"("Hello \ world")"s; // std::u32string from a raw const char32_t*, encoded as UTF-32


  // ASCII smiling face
  const char*     s1 = ":-)";

  // UTF-16 (on Windows) encoded WINKING FACE (U+1F609)
  const wchar_t*  s2 = L"
    
            

© Habrahabr.ru