"Делает ли std :: string то, что должен?" Как вы думаете, что это должно сделать?
Р. Мартиньо Фернандес
2
Я использую utfcpp.sourceforge.net для моих нужд utf8. Это простой заголовочный файл, который предоставляет итераторы для строк Unicode.
Fscan
2
std :: string должен хранить байты, то есть последовательность кодовых единиц кодировки UTF-8. Да, именно так и есть, с самого начала. utf8everywhere.org
Павел Радзивиловский
3
Самые большие потенциальные проблемы с поддержкой Unicode заключаются в Unicode и его использовании в самих информационных технологиях. Unicode не подходит (и не предназначен) для того, для чего он используется. Unicode предназначен для воспроизведения каждого возможного глифа, который был написан кем-то кем-то, в какое-то время с каждым маловероятным и педантичным нюансом, включая 3 или 4 различных значения и 3 или 4 различных способа составления одного и того же символа. Оно не предназначено для того, чтобы быть полезным для использования в повседневном языке, и оно не предназначено для того, чтобы быть применимым или быть легко или однозначно обработанным.
Деймон
11
Да, он предназначен для использования на повседневном языке. Мой по крайней мере. И ваш, скорее всего, тоже. Просто получается, что обработка человеческого текста в общем виде - очень сложная задача. Невозможно даже однозначно определить, что такое персонаж. Общее воспроизведение глифа даже не является частью хартии Unicode.
Жан-Дени Мюйс
Ответы:
267
Насколько хорошо стандартная библиотека C ++ поддерживает Unicode?
Жутко.
Быстрый просмотр библиотечных средств, которые могут обеспечить поддержку Unicode, дает мне этот список:
Библиотека строк
Библиотека локализации
Библиотека ввода / вывода
Библиотека регулярных выражений
Я думаю, что все, кроме первого, оказывают ужасную поддержку. Я вернусь к этому более подробно после короткого обхода других ваших вопросов.
Делает std::stringто, что должен?
Да. Согласно стандарту C ++, это то, что std::stringи его братья и сестры должны делать:
Шаблон класса basic_stringописывает объекты, которые могут хранить последовательность, состоящую из различного числа произвольных символов, похожих на символы, с первым элементом последовательности в нулевой позиции.
Ну, std::stringэто прекрасно. Предоставляет ли это какую-либо специфическую для Unicode функциональность? Нет .
Должно ли это? Возможно нет. std::stringхорошо, как последовательность charобъектов. Это полезно; Единственное раздражение в том, что это очень низкоуровневое представление текста, а стандартный C ++ не обеспечивает более высокого уровня.
Как мне это использовать?
Используйте это как последовательность charобъектов; притворяться, что это что-то еще, должно закончиться болью.
Где потенциальные проблемы?
Повсюду? Посмотрим...
Библиотека строк
Библиотека строк предоставляет нам basic_string, что является просто последовательностью того, что стандарт называет «объектами, подобными символу». Я называю их кодовыми единицами. Если вы хотите просмотреть текст на высоком уровне, это не то, что вы ищете. Это вид текста, подходящего для сериализации / десериализации / хранения.
Он также предоставляет некоторые инструменты из библиотеки C, которые можно использовать для преодоления разрыва между узким миром и миром Unicode: c16rtomb/ mbrtoc16и c32rtomb/ mbrtoc32.
Библиотека локализации
Библиотека локализации по-прежнему считает, что один из этих «похожих на символы» объектов равен одному «символу». Это, конечно, глупо и делает невозможным правильную работу многих вещей, кроме небольшого подмножества Юникода, такого как ASCII.
Рассмотрим, например, что стандарт называет «удобными интерфейсами» в <locale>заголовке:
Как вы ожидаете, что любая из этих функций правильно классифицирует, скажем, U + 1F34C as, как в u8"🍌"или u8"\U0001F34C"? Нет никакого способа, которым это когда-либо будет работать, потому что эти функции принимают только одну единицу кода в качестве ввода.
Это может работать с соответствующей локалью, если вы используете char32_tтолько: U'\U0001F34C'это единица кода в UTF-32.
Однако это по-прежнему означает, что вы получаете только простые преобразования регистров с помощью toupperи tolower, что, например, недостаточно для некоторых немецких локалей: от «ß» в верхнем регистре до «SS» ☦, но вы toupperможете вернуть только одну единицу кода символа .
wstring_convertиспользуется для преобразования между строками в одной заданной кодировке в строки в другой заданной кодировке. В этом преобразовании участвуют два типа строк, которые в стандарте называются байтовой строкой и широкой строкой. Поскольку эти термины действительно вводят в заблуждение, я предпочитаю использовать «сериализованный» и «десериализованный» соответственно †.
Кодировки для преобразования определяются с помощью codecvt (фасета преобразования кода), передаваемого в качестве аргумента типа шаблона wstring_convert.
wbuffer_convertвыполняет аналогичную функцию, но как широкий десериализованный потоковый буфер, который оборачивает байтовый сериализованный буфер потока Любой ввод / вывод выполняется через базовый байтовый сериализованный потоковый буфер с преобразованиями в и из кодировок, заданных аргументом codecvt. Запись сериализуется в этот буфер, а затем записывает из него, а чтение читает в буфер и затем десериализуется из него.
Стандарт предусматривает некоторые шаблоны классов codecvt для использования этих средств: codecvt_utf8, codecvt_utf16, codecvt_utf8_utf16, и некоторые codecvtспециализации. Вместе эти стандартные аспекты обеспечивают все следующие преобразования. (Примечание: в следующем списке кодировка слева всегда является сериализованной строкой / streambuf, а кодировка справа всегда десериализованной строкой / streambuf; стандарт допускает преобразования в обоих направлениях).
UTF-8 ↔ UCS-2 с codecvt_utf8<char16_t>и codecvt_utf8<wchar_t>где sizeof(wchar_t) == 2;
узкий, широкий с codecvt<wchar_t, char_t, mbstate_t>
не с codecvt<char, char, mbstate_t>.
Некоторые из них полезны, но здесь есть много неловких вещей.
Прежде всего - святой верховный суррогат! эта схема именования является грязной.
Затем есть большая поддержка UCS-2. UCS-2 - это кодировка из Unicode 1.0, которая была заменена в 1996 году, потому что она поддерживает только базовую многоязычную плоскость. Почему комитет счел желательным сосредоточиться на кодировке, которая была заменена более 20 лет назад, я не знаю ‡. Не то, чтобы поддержка большего количества кодировок была плохой или что-то в этом роде, но UCS-2 появляется здесь слишком часто.
Я бы сказал, что char16_tон предназначен для хранения кодовых блоков UTF-16. Однако это одна часть стандарта, которая считает иначе. codecvt_utf8<char16_t>не имеет ничего общего с UTF-16. Например, wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")скомпилируется нормально, но без каких-либо условий: вход будет обрабатываться как строка UCS-2 u"\xD83C\xDF4C", которую нельзя преобразовать в UTF-8, потому что UTF-8 не может кодировать любое значение в диапазоне 0xD800-0xDFFF.
Тем не менее, на фронте UCS-2 нет способа чтения из потока байтов UTF-16 в строку UTF-16 с этими аспектами. Если у вас есть последовательность байтов UTF-16, вы не можете десериализовать ее в строку char16_t. Это удивительно, потому что это более или менее преобразование личности. Однако еще более удивительным является тот факт, что существует поддержка десериализации из потока UTF-16 в строку UCS-2 codecvt_utf16<char16_t>, что на самом деле является преобразованием с потерями.
Тем не менее, поддержка UTF-16 в качестве байтов довольно хороша: она поддерживает обнаружение бесконечности в спецификации или ее явное выделение в коде. Он также поддерживает создание вывода с и без спецификации.
Есть еще несколько интересных возможностей для конвертации. Невозможно десериализовать поток байтов или строку UTF-16 в строку UTF-8, поскольку UTF-8 никогда не поддерживается в качестве десериализованной формы.
И здесь узкий / широкий мир полностью отделен от мира UTF / UCS. Не существует преобразований между узкими / широкими кодировками старого стиля и любыми кодировками Unicode.
Библиотека ввода / вывода
Библиотеку ввода / вывода можно использовать для чтения и записи текста в кодировках Unicode с использованием средств wstring_convertи wbuffer_convert, описанных выше. Я не думаю, что есть еще что-то, что должно было бы поддерживаться этой частью стандартной библиотеки.
Библиотека регулярных выражений
Ранее я уже разъяснял проблемы с регулярными выражениями в C ++ и Unicode в Stack Overflow. Я не буду повторять все эти пункты здесь, а просто скажу, что регулярные выражения C ++ не имеют поддержки Unicode уровня 1, что является минимальным условием для их использования, не прибегая к повсеместному использованию UTF-32.
Это оно?
Да это оно. Это существующий функционал. Существует множество функций Unicode, которые нигде не встречаются, такие как алгоритмы нормализации или сегментации текста.
U + 1F4A9 . Есть ли способ получить лучшую поддержку Unicode в C ++?
† Неудивительно, что строка байтов - это строка байтов, т. Е. charОбъектов. Однако, в отличие от литерала с широкой строкой , который всегда является массивом wchar_tобъектов, «широкая строка» в этом контексте не обязательно является строкой wchar_tобъектов. На самом деле, стандарт никогда не определяет явно, что означает «широкая строка», поэтому нам остается только угадывать значение от использования. Поскольку стандартная терминология небрежная и запутанная, я использую свою собственную во имя ясности.
Кодировки, подобные UTF-16, могут храниться в виде последовательностей char16_t, которые затем не имеют порядкового номера; или они могут быть сохранены как последовательности байтов, которые имеют порядковый номер (каждая последовательная пара байтов может представлять различное char16_tзначение в зависимости от порядкового номера). Стандарт поддерживает обе эти формы. Последовательность char16_tболее полезна для внутренних манипуляций в программе. Последовательность байтов - это способ обмена такими строками с внешним миром. Термины, которые я буду использовать вместо «байт» и «широкий», таким образом, «сериализуются» и «десериализуются».
‡ Если вы собираетесь сказать «но Windows!» держи свой 🐎🐎 . Все версии Windows начиная с Windows 2000 используют UTF-16.
☦ Да, я знаю про grosses Eszett ( ẞ ), но даже если бы вы за ночь поменяли все немецкие языки на прописные буквы ε, есть еще множество других случаев, когда это не получится. Попробуйте прописные буквы U + FB00 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟɪɢᴀᴛᴜʀᴇ ғғ. Там нет ʟᴀᴛɪɴ ᴄᴀᴘɪᴛᴀʟ ʟɪɢᴀᴛᴜʀᴇ ғғ; это только прописные буквы до двух Fs. Или U + 01F0 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟᴇᴛᴛᴇʀ ᴊ ᴡɪᴛʜ ᴄᴀʀᴏɴ; нет заранее составленного капитала; это только заглавные буквы к заглавной букве J и объединяющему caron.
Чем больше я об этом читаю, тем больше у меня возникает чувство, что я ничего не понимаю во всем этом. Я прочитал большую часть этого материала пару месяцев назад и все еще чувствую, что снова и снова открываю для себя все это ... Чтобы не усложнять для моего бедного мозга, который сейчас немного болит, все эти советы на utf8everywhere все еще актуальны, право? Если я «просто» хочу, чтобы мои пользователи могли открывать и записывать файлы независимо от их системных настроек, я могу спросить у них имя файла, сохранить его в std :: string, и все должно работать правильно, даже в Windows? Извините, что спросил (снова) ...
Uflex
5
@Uflex Все, что вы действительно можете сделать с помощью std :: string, это обработать его как двоичный двоичный объект. В правильной реализации Unicode ни внутреннее (потому что оно скрыто глубоко в деталях реализации), ни внешнее кодирование не имеет значения (ну, конечно, вам все еще нужно иметь доступный кодер / декодер).
Cat Plus Plus
3
@ Уфлекс может быть. Я не знаю, если следующий совет вы не понимаете, это хорошая идея.
@ graham.reeds хаха, спасибо, но я знал об этом. Проверьте раздел «Благодарности»;)
Р. Мартиньо Фернандес
40
Unicode не поддерживается стандартной библиотекой (для любого разумного значения поддерживается).
std::stringне лучше , чем std::vector<char>: она совершенно не обращая внимания на Unicode (или любое другое представление / кодирование) и просто рассматривать его содержимое в виде сгустка байтов.
Если вам нужно хранить и хранить только капли , это работает довольно хорошо; но как только вы захотите использовать функциональность Unicode (количество точек кода , количество графем и т. д.), вам не повезло.
Единственная всеобъемлющая библиотека, о которой я знаю, это ICU . Интерфейс C ++ произошел от Java, поэтому он далеко не идиоматичен.
@Uflex: со страницы, на которую вы ссылаетесь. Для достижения этой цели Boost.Locale использует современную библиотеку Unicode и локализации: ICU - Международные компоненты для Unicode.
@SuperflyJon: Да, но, согласно той же странице, поддержка Unicode для бэкэндов не-ICU "строго ограничена".
Матье М.
24
Вы можете безопасно хранить UTF-8 в std::string(или в char[]или или char*, в этом отношении), потому что Unicode NUL (U + 0000) является нулевым байтом в UTF-8, и это единственный способ нулевого байт может встречаться в UTF-8. Следовательно, ваши строки UTF-8 будут должным образом завершены в соответствии со всеми строковыми функциями C и C ++, и вы можете перебирать их с помощью iostreams C ++ (включая std::coutи std::cerr, если ваш языковой стандарт UTF-8).
То, что вы не можете сделать std::stringдля UTF-8, это получить длину в кодовых точках. std::string::size()сообщит вам длину строки в байтах , которая равна только числу кодовых точек, когда вы находитесь в подмножестве ASCII UTF-8.
Если вам нужно работать со строками UTF-8 на уровне кодовой точки (т.е. не просто хранить и распечатывать их), или если вы имеете дело с UTF-16, который, вероятно, имеет много внутренних нулевых байтов, вам нужно изучить типы строк широких символов.
std::stringможет быть просто брошено в iostreams со встроенными нулями.
Р. Мартиньо Фернандес
3
Это полностью предназначено. Это не ломается c_str()вообще, потому что size()все еще работает. Только сломанные API (то есть те, которые не могут обрабатывать встроенные нули, как большая часть мира Си) ломаются.
Р. Мартиньо Фернандес
1
Внедренные нулевые значения прерываются, c_str()поскольку c_str()предполагается, что они возвращают данные в виде строки C с нулевым символом в конце - что невозможно из-за того, что строки C не могут иметь встроенные нули.
Uckelman
4
Уже нет. c_str()теперь просто возвращает то же самое, что data()и все. API, которые принимают размер, могут потреблять его. API, которые не, не могут.
Р. Мартиньо Фернандес
6
С небольшим отличием, которое c_str()гарантирует, что за результатом следует NUL-символоподобный объект, и я не думаю, что data()это так. Нет, похоже, data()сейчас тоже так делает. (Конечно, это не обязательно для API, которые используют размер вместо того, чтобы выводить его из поиска терминатора)
К сожалению, поддержка в стандартной библиотеке для неоднородных кодировок (таких как UTF-8) все еще плоха. Например, нет хорошего способа получить длину (в кодовых точках) строки UTF-8.
Так нужно ли нам использовать std :: wstring для имен файлов, если мы хотим поддерживать нелатинские языки? Потому что новые строковые литералы здесь не очень помогают, так как строка обычно приходит от пользователя ...
Uflex
7
@Uflex std::stringможет содержать строку UTF-8 без проблем, но, например, lengthметод возвращает количество байтов в строке, а не количество кодовых точек.
Какой-то программист чувак
8
Честно говоря, получение длины в кодовых точках строки не имеет большого применения. Длина в байтах может использоваться, например, для правильного предварительного выделения буферов.
Р. Мартиньо Фернандес
2
Количество кодовых точек в строке UTF-8 не очень интересное число: можно написать ñ«LATIN SMALL LETTER N WITH TILDE» (U + 00F1) (то есть одна кодовая точка) или «LATIN SMALL LETTER N» ( U + 006E), за которым следует «КОМБИНИРОВАНИЕ ТИЛЬДЫ» (U + 0303), что составляет две кодовые точки.
Мартин Боннер поддерживает Монику
Все эти комментарии о том, что «вам это не нужно и вам не нужно», как «количество неважных кодовых точек» и т. Д., Звучат для меня немного странно. Как только вы напишите синтаксический анализатор, который должен анализировать исходный код utf8, дело доходит до спецификации синтаксического анализатора, считает ли он LATIN SMALL LETTER N' == (U+006E) followed by 'COMBINING TILDE' (U+0303).
BitTickler
4
Тем не менее, есть очень полезная библиотека называется крошечным-utf8 , который является в основном заменой для std::string/ std::wstring. Он призван восполнить пробел в еще отсутствующем классе контейнера utf8-string.
Это может быть наиболее удобным способом работы со строками utf8 (то есть без нормализации юникода и тому подобного). Вы комфортно работаете с кодовыми точками , в то время как ваша строка остается закодированной в кодированных charдлинах серий s.
Ответы:
Жутко.
Быстрый просмотр библиотечных средств, которые могут обеспечить поддержку Unicode, дает мне этот список:
Я думаю, что все, кроме первого, оказывают ужасную поддержку. Я вернусь к этому более подробно после короткого обхода других ваших вопросов.
Да. Согласно стандарту C ++, это то, что
std::string
и его братья и сестры должны делать:Ну,
std::string
это прекрасно. Предоставляет ли это какую-либо специфическую для Unicode функциональность? Нет .Должно ли это? Возможно нет.
std::string
хорошо, как последовательностьchar
объектов. Это полезно; Единственное раздражение в том, что это очень низкоуровневое представление текста, а стандартный C ++ не обеспечивает более высокого уровня.Используйте это как последовательность
char
объектов; притворяться, что это что-то еще, должно закончиться болью.Повсюду? Посмотрим...
Библиотека строк
Библиотека строк предоставляет нам
basic_string
, что является просто последовательностью того, что стандарт называет «объектами, подобными символу». Я называю их кодовыми единицами. Если вы хотите просмотреть текст на высоком уровне, это не то, что вы ищете. Это вид текста, подходящего для сериализации / десериализации / хранения.Он также предоставляет некоторые инструменты из библиотеки C, которые можно использовать для преодоления разрыва между узким миром и миром Unicode:
c16rtomb
/mbrtoc16
иc32rtomb
/mbrtoc32
.Библиотека локализации
Библиотека локализации по-прежнему считает, что один из этих «похожих на символы» объектов равен одному «символу». Это, конечно, глупо и делает невозможным правильную работу многих вещей, кроме небольшого подмножества Юникода, такого как ASCII.
Рассмотрим, например, что стандарт называет «удобными интерфейсами» в
<locale>
заголовке:Как вы ожидаете, что любая из этих функций правильно классифицирует, скажем, U + 1F34C as, как в
u8"🍌"
илиu8"\U0001F34C"
? Нет никакого способа, которым это когда-либо будет работать, потому что эти функции принимают только одну единицу кода в качестве ввода.Это может работать с соответствующей локалью, если вы используете
char32_t
только:U'\U0001F34C'
это единица кода в UTF-32.Однако это по-прежнему означает, что вы получаете только простые преобразования регистров с помощью
toupper
иtolower
, что, например, недостаточно для некоторых немецких локалей: от «ß» в верхнем регистре до «SS» ☦, но выtoupper
можете вернуть только одну единицу кодасимвола.Далее,
wstring_convert
/wbuffer_convert
и стандартные аспекты преобразования кода.wstring_convert
используется для преобразования между строками в одной заданной кодировке в строки в другой заданной кодировке. В этом преобразовании участвуют два типа строк, которые в стандарте называются байтовой строкой и широкой строкой. Поскольку эти термины действительно вводят в заблуждение, я предпочитаю использовать «сериализованный» и «десериализованный» соответственно †.Кодировки для преобразования определяются с помощью codecvt (фасета преобразования кода), передаваемого в качестве аргумента типа шаблона
wstring_convert
.wbuffer_convert
выполняет аналогичную функцию, но какширокийдесериализованный потоковый буфер, который оборачиваетбайтовыйсериализованный буфер потока Любой ввод / вывод выполняется через базовыйбайтовыйсериализованный потоковый буфер с преобразованиями в и из кодировок, заданных аргументом codecvt. Запись сериализуется в этот буфер, а затем записывает из него, а чтение читает в буфер и затем десериализуется из него.Стандарт предусматривает некоторые шаблоны классов codecvt для использования этих средств:
codecvt_utf8
,codecvt_utf16
,codecvt_utf8_utf16
, и некоторыеcodecvt
специализации. Вместе эти стандартные аспекты обеспечивают все следующие преобразования. (Примечание: в следующем списке кодировка слева всегда является сериализованной строкой / streambuf, а кодировка справа всегда десериализованной строкой / streambuf; стандарт допускает преобразования в обоих направлениях).codecvt_utf8<char16_t>
иcodecvt_utf8<wchar_t>
гдеsizeof(wchar_t) == 2
;codecvt_utf8<char32_t>
,codecvt<char32_t, char, mbstate_t>
иcodecvt_utf8<wchar_t>
гдеsizeof(wchar_t) == 4
;codecvt_utf16<char16_t>
иcodecvt_utf16<wchar_t>
гдеsizeof(wchar_t) == 2
;codecvt_utf16<char32_t>
иcodecvt_utf16<wchar_t>
гдеsizeof(wchar_t) == 4
;codecvt_utf8_utf16<char16_t>
,codecvt<char16_t, char, mbstate_t>
иcodecvt_utf8_utf16<wchar_t>
гдеsizeof(wchar_t) == 2
;codecvt<wchar_t, char_t, mbstate_t>
codecvt<char, char, mbstate_t>
.Некоторые из них полезны, но здесь есть много неловких вещей.
Прежде всего - святой верховный суррогат! эта схема именования является грязной.
Затем есть большая поддержка UCS-2. UCS-2 - это кодировка из Unicode 1.0, которая была заменена в 1996 году, потому что она поддерживает только базовую многоязычную плоскость. Почему комитет счел желательным сосредоточиться на кодировке, которая была заменена более 20 лет назад, я не знаю ‡. Не то, чтобы поддержка большего количества кодировок была плохой или что-то в этом роде, но UCS-2 появляется здесь слишком часто.
Я бы сказал, что
char16_t
он предназначен для хранения кодовых блоков UTF-16. Однако это одна часть стандарта, которая считает иначе.codecvt_utf8<char16_t>
не имеет ничего общего с UTF-16. Например,wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")
скомпилируется нормально, но без каких-либо условий: вход будет обрабатываться как строка UCS-2u"\xD83C\xDF4C"
, которую нельзя преобразовать в UTF-8, потому что UTF-8 не может кодировать любое значение в диапазоне 0xD800-0xDFFF.Тем не менее, на фронте UCS-2 нет способа чтения из потока байтов UTF-16 в строку UTF-16 с этими аспектами. Если у вас есть последовательность байтов UTF-16, вы не можете десериализовать ее в строку
char16_t
. Это удивительно, потому что это более или менее преобразование личности. Однако еще более удивительным является тот факт, что существует поддержка десериализации из потока UTF-16 в строку UCS-2codecvt_utf16<char16_t>
, что на самом деле является преобразованием с потерями.Тем не менее, поддержка UTF-16 в качестве байтов довольно хороша: она поддерживает обнаружение бесконечности в спецификации или ее явное выделение в коде. Он также поддерживает создание вывода с и без спецификации.
Есть еще несколько интересных возможностей для конвертации. Невозможно десериализовать поток байтов или строку UTF-16 в строку UTF-8, поскольку UTF-8 никогда не поддерживается в качестве десериализованной формы.
И здесь узкий / широкий мир полностью отделен от мира UTF / UCS. Не существует преобразований между узкими / широкими кодировками старого стиля и любыми кодировками Unicode.
Библиотека ввода / вывода
Библиотеку ввода / вывода можно использовать для чтения и записи текста в кодировках Unicode с использованием средств
wstring_convert
иwbuffer_convert
, описанных выше. Я не думаю, что есть еще что-то, что должно было бы поддерживаться этой частью стандартной библиотеки.Библиотека регулярных выражений
Ранее я уже разъяснял проблемы с регулярными выражениями в C ++ и Unicode в Stack Overflow. Я не буду повторять все эти пункты здесь, а просто скажу, что регулярные выражения C ++ не имеют поддержки Unicode уровня 1, что является минимальным условием для их использования, не прибегая к повсеместному использованию UTF-32.
Да это оно. Это существующий функционал. Существует множество функций Unicode, которые нигде не встречаются, такие как алгоритмы нормализации или сегментации текста.
Обычные подозреваемые: ICU и Boost.Locale .
† Неудивительно, что строка байтов - это строка байтов, т. Е.
char
Объектов. Однако, в отличие от литерала с широкой строкой , который всегда является массивомwchar_t
объектов, «широкая строка» в этом контексте не обязательно является строкойwchar_t
объектов. На самом деле, стандарт никогда не определяет явно, что означает «широкая строка», поэтому нам остается только угадывать значение от использования. Поскольку стандартная терминология небрежная и запутанная, я использую свою собственную во имя ясности.Кодировки, подобные UTF-16, могут храниться в виде последовательностей
char16_t
, которые затем не имеют порядкового номера; или они могут быть сохранены как последовательности байтов, которые имеют порядковый номер (каждая последовательная пара байтов может представлять различноеchar16_t
значение в зависимости от порядкового номера). Стандарт поддерживает обе эти формы. Последовательностьchar16_t
более полезна для внутренних манипуляций в программе. Последовательность байтов - это способ обмена такими строками с внешним миром. Термины, которые я буду использовать вместо «байт» и «широкий», таким образом, «сериализуются» и «десериализуются».‡ Если вы собираетесь сказать «но Windows!» держи свой 🐎🐎 . Все версии Windows начиная с Windows 2000 используют UTF-16.
☦ Да, я знаю про grosses Eszett ( ẞ ), но даже если бы вы за ночь поменяли все немецкие языки на прописные буквы ε, есть еще множество других случаев, когда это не получится. Попробуйте прописные буквы U + FB00 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟɪɢᴀᴛᴜʀᴇ ғғ. Там нет ʟᴀᴛɪɴ ᴄᴀᴘɪᴛᴀʟ ʟɪɢᴀᴛᴜʀᴇ ғғ; это только прописные буквы до двух Fs. Или U + 01F0 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟᴇᴛᴛᴇʀ ᴊ ᴡɪᴛʜ ᴄᴀʀᴏɴ; нет заранее составленного капитала; это только заглавные буквы к заглавной букве J и объединяющему caron.
источник
Unicode не поддерживается стандартной библиотекой (для любого разумного значения поддерживается).
std::string
не лучше , чемstd::vector<char>
: она совершенно не обращая внимания на Unicode (или любое другое представление / кодирование) и просто рассматривать его содержимое в виде сгустка байтов.Если вам нужно хранить и хранить только капли , это работает довольно хорошо; но как только вы захотите использовать функциональность Unicode (количество точек кода , количество графем и т. д.), вам не повезло.
Единственная всеобъемлющая библиотека, о которой я знаю, это ICU . Интерфейс C ++ произошел от Java, поэтому он далеко не идиоматичен.
источник
Вы можете безопасно хранить UTF-8 в
std::string
(или вchar[]
или илиchar*
, в этом отношении), потому что Unicode NUL (U + 0000) является нулевым байтом в UTF-8, и это единственный способ нулевого байт может встречаться в UTF-8. Следовательно, ваши строки UTF-8 будут должным образом завершены в соответствии со всеми строковыми функциями C и C ++, и вы можете перебирать их с помощью iostreams C ++ (включаяstd::cout
иstd::cerr
, если ваш языковой стандарт UTF-8).То, что вы не можете сделать
std::string
для UTF-8, это получить длину в кодовых точках.std::string::size()
сообщит вам длину строки в байтах , которая равна только числу кодовых точек, когда вы находитесь в подмножестве ASCII UTF-8.Если вам нужно работать со строками UTF-8 на уровне кодовой точки (т.е. не просто хранить и распечатывать их), или если вы имеете дело с UTF-16, который, вероятно, имеет много внутренних нулевых байтов, вам нужно изучить типы строк широких символов.
источник
std::string
может быть просто брошено в iostreams со встроенными нулями.c_str()
вообще, потому чтоsize()
все еще работает. Только сломанные API (то есть те, которые не могут обрабатывать встроенные нули, как большая часть мира Си) ломаются.c_str()
посколькуc_str()
предполагается, что они возвращают данные в виде строки C с нулевым символом в конце - что невозможно из-за того, что строки C не могут иметь встроенные нули.c_str()
теперь просто возвращает то же самое, чтоdata()
и все. API, которые принимают размер, могут потреблять его. API, которые не, не могут.c_str()
гарантирует, что за результатом следует NUL-символоподобный объект, и я не думаю, чтоdata()
это так. Нет, похоже,data()
сейчас тоже так делает. (Конечно, это не обязательно для API, которые используют размер вместо того, чтобы выводить его из поиска терминатора)C ++ 11 имеет несколько новых типов литеральных строк для Unicode.
К сожалению, поддержка в стандартной библиотеке для неоднородных кодировок (таких как UTF-8) все еще плоха. Например, нет хорошего способа получить длину (в кодовых точках) строки UTF-8.
источник
std::string
может содержать строку UTF-8 без проблем, но, например,length
метод возвращает количество байтов в строке, а не количество кодовых точек.ñ
«LATIN SMALL LETTER N WITH TILDE» (U + 00F1) (то есть одна кодовая точка) или «LATIN SMALL LETTER N» ( U + 006E), за которым следует «КОМБИНИРОВАНИЕ ТИЛЬДЫ» (U + 0303), что составляет две кодовые точки.LATIN SMALL LETTER N'
==(U+006E) followed by 'COMBINING TILDE' (U+0303)
.Тем не менее, есть очень полезная библиотека называется крошечным-utf8 , который является в основном заменой для
std::string
/std::wstring
. Он призван восполнить пробел в еще отсутствующем классе контейнера utf8-string.Это может быть наиболее удобным способом работы со строками utf8 (то есть без нормализации юникода и тому подобного). Вы комфортно работаете с кодовыми точками , в то время как ваша строка остается закодированной в кодированных
char
длинах серий s.источник