Как использовать 3-х и 4-х байтовые символы Юникода со стандартными строками C ++?

В стандарте C ++ мы имеем char а также wchar_t для хранения персонажей. char может хранить значения между 0x00 а также 0xFF. А также wchar_t может хранить значения между 0x0000 а также 0xFFFF, std::string использования char, поэтому он может хранить только 1-байтовые символы. std::wstring использования wchar_t, так что он может хранить символы до 2-байтовой ширины. Это то, что я знаю о строках в C ++. Пожалуйста, поправьте меня, если я сказал что-то не так до этого момента.

Я читаю статья для UTF-8 в Википедии, и я узнал, что некоторые символы Юникода занимают до 4-байтового пространства. Например, китайский иероглиф �� имеет кодовую точку Unicode 0x24B62, который занимает 3-байтовое пространство в памяти.

Есть ли контейнер строки STL для работы с такими символами? Я ищу что-то вроде std::string32, Также у нас было main() для точки входа ASCII, wmain() для точки входа с поддержкой 16-битных символов; какую точку входа мы используем для 3-х и 4-х байтового кода, поддерживаемого Unicode?

Можете ли вы добавить крошечный пример?

(Моя ОС: Windows 7 x64)

10

Решение

Сначала вам нужно лучше понять Unicode. Конкретные ответы на ваши вопросы находятся внизу.

Концепции

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

  • байт
  • кодовая единица
  • кодовая точка
  • абстрактный характер
  • Персонаж воспринимается пользователем

Байт — это наименьшая адресуемая единица памяти. Обычно сегодня 8 бит, способных хранить до 256 различных значений. По определению символ — это один байт.

Единица кода — это наименьшая единица фиксированного размера данных, используемая для хранения текста. Если вас не интересует содержание текста, и вы просто хотите скопировать его куда-нибудь или рассчитать, сколько памяти использует текст, тогда вам небезразличны единицы кода. В противном случае кодовые единицы не очень полезны.

Кодовая точка представляет отдельный элемент набора символов. Какие бы «символы» ни были в наборе символов, им всем присваивается уникальный номер, и всякий раз, когда вы видите кодированный конкретный номер, вы знаете, с каким членом набора символов вы имеете дело.

Абстрактный символ — это объект, имеющий значение в лингвистической системе, и он отличается от своего представления или любых кодовых точек, присвоенных этому значению.

Пользователь воспринимает символы, как они звучат; что пользователь считает персонажем в любой языковой системе, которую он использует.

В былые времена, char представлены все эти вещи: char это по определению байт, в char* Строки кодовые единицы chars, наборы символов были небольшими, поэтому 256 значений, представляемых char Было достаточно представить каждого участника, и поддерживаемые лингвистические системы были просты, поэтому члены наборов символов в основном представляли символы, которые пользователи хотели использовать напрямую.

Но эта простая система с char представлять почти все было недостаточно для поддержки более сложных систем.


Первая проблема состояла в том, что некоторые языки используют намного больше, чем 256 символов. Таким образом, «широкие» символы были введены. Широкие символы по-прежнему использовали один тип для представления четырех представленных выше концепций, кодовых единиц, кодовых точек, абстрактных символов и воспринимаемых пользователем символов. Однако широкие символы больше не являются одиночными байтами. Считалось, что это самый простой способ поддержки больших наборов символов.

Код может быть в основном таким же, за исключением того, что он будет работать с широкими символами вместо char,

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

Широкие символы имеют еще одну проблему. Так как они увеличивают размер блока кода, они увеличивают пространство, используемое для каждого символа. Если кто-то хочет иметь дело с текстом, который может быть адекватно представлен единичными единицами байтового кода, но должен использовать систему широких символов, тогда объем используемой памяти будет выше, чем в случае единичных единиц байтового кода. Поэтому желательно, чтобы широкие символы не были слишком широкими. В то же время широкие символы должны быть достаточно широкими, чтобы обеспечить уникальное значение для каждого члена набора символов.

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

Таким образом, чтобы подвести итог, первоначально не было необходимости различать байты, единицы кода, кодовые точки, абстрактные символы и воспринимаемые пользователем символы. Однако со временем стало необходимо различать каждое из этих понятий.


Кодировки

До вышесказанного текстовые данные было просто хранить. Каждый воспринимаемый пользователем символ соответствует абстрактному символу, который имеет значение кода. Было достаточно символов, 256 значений было достаточно. Таким образом, просто сохраняются номера кодовых точек, соответствующие желаемым воспринимаемым пользователем символам, непосредственно в байтах. Позже, с широкими символами, значения, соответствующие принятым пользователем символам, были сохранены непосредственно как целые числа больших размеров, например, 16 битов.

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

Например, кодирование UTF-8 может принимать наиболее часто используемые кодовые точки Unicode и представлять их с помощью одной однобайтовой кодовой единицы. Менее распространенные кодовые точки хранятся с использованием двух однобайтовых кодовых единиц. Кодовые точки, которые все еще менее распространены, хранятся с использованием трех или четырех кодовых единиц.

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

Существует много разных кодировок, и для преобразования данных с использованием этих кодировок в абстрактные символы необходимо знать правильный метод декодирования. Сохраненные значения фактически бессмысленны, если вы не знаете, какая кодировка использовалась для преобразования значений кодовых точек в единицы кода.


Важным следствием кодировок является то, что вам необходимо знать, являются ли конкретные манипуляции с закодированными данными допустимыми или значимыми.

Например, если вы хотите получить «размер» строки, считаете ли вы байты, единицы кода, абстрактные символы или воспринимаемые пользователем символы? std::string::size() считает единицы кода, и если вам нужен другой счет, то вам нужно использовать другой метод.

В качестве другого примера, если вы разбиваете закодированную строку, вам нужно знать, делаете ли вы это так, чтобы результат в этой кодировке был действительным и чтобы значение данных не изменилось непреднамеренно. Например, вы можете разделить кодовые блоки, принадлежащие к одной и той же кодовой точке, что приведет к неправильному кодированию. Или вы можете разделить кодовые точки, которые должны быть объединены, чтобы представить воспринимаемый пользователем символ и, таким образом, создать данные, которые пользователь сочтет неверными.

ответы

сегодня char а также wchar_t можно считать только единицами кода. Дело в том, что char только один байт не препятствует представлению кодовых точек, занимающих два, три или четыре байта. Вы просто должны использовать два, три или четыре charв последовательности. Вот как UTF-8 должен был работать. Аналогично, платформы, которые используют два байта wchar_t для представления UTF-16 просто используйте два wchar_t подряд, когда это необходимо. Фактические значения char а также wchar_t не представляйте индивидуально кодовые точки Unicode. Они представляют значения кодовых единиц, которые являются результатом кодирования кодовых точек. Например. Кодовая точка Unicode U + 0400 кодируется в две кодовые единицы в UTF-8 -> 0xD0 0x80, Кодовая точка Unicode U + 24B62 аналогично кодируется в виде четырех кодовых единиц 0xF0 0xA4 0xAD 0xA2,

Так что вы можете использовать std::string для хранения данных в кодировке UTF-8.

На винде main() поддерживает не только ASCII, но и любую систему char кодировка есть. К сожалению, Windows не поддерживает UTF-8 как систему char кодирование, как это делают другие платформы, так что вы ограничены устаревшими кодировками, такими как cp1252 или любым другим, что ваша система настроена для использования. Однако вы можете использовать вызов Win32 API для прямого доступа к параметрам командной строки UTF-16 вместо использования main()s argc а также argv параметры. Увидеть GetCommandLineW() а также CommandLineToArgvW.

wmain()«s argv Параметр полностью поддерживает Unicode. 16-битные кодовые единицы хранятся в wchar_t на Windows есть кодовые единицы UTF-16. Windows API изначально использует UTF-16, поэтому с ним довольно легко работать в Windows. wmain() хотя это нестандартно, поэтому полагаться на это нельзя будет портативно.

20

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

Размер и значение wchar_t определяется реализацией. В Windows он, как вы говорите, 16-битный, в Unix-подобных системах он часто 32-битный, но не всегда.

В этом отношении компилятору разрешено делать свое дело и выбирать другой размер для wchar_t чем то, что говорит система — она ​​просто не будет ABI-совместимой с остальной системой.

C ++ 11 обеспечивает std::u32string, который предназначен для представления строк кодовых точек Unicode. Я считаю, что достаточно недавние компиляторы Microsoft включают его. Он имеет несколько ограниченное использование, поскольку системные функции Microsoft ожидают 16-битные широкие символы (a.k.a UTF-16le), а не 32-битные кодовые точки Unicode (a.k.a UTF-32, UCS-4).

Вы упоминаете UTF-8, хотя: данные в кодировке UTF-8 могут храниться в обычном std::string, Конечно, поскольку это кодирование переменной длины, вы не можете получить доступ к кодовым точкам Юникода по индексу, вы можете получить доступ только к байтам по индексу. Но вы, как правило, пишете свой код, чтобы в любом случае не нужно было обращаться к кодовым точкам, даже если вы используете u32string, Кодовые точки Unicode не соответствуют 1-1 печатным символам («графемам») из-за наличия комбинированных меток в Unicode, поэтому многие маленькие хитрости вы играете со строками, когда учитесь программировать (обращать их, искать подстроки) не так легко работать с данными Unicode, независимо от того, где вы их храните.

Персонаж, как вы говорите, \ u24B62. Это UTF-8, закодированный как серия четыре байт, а не три: F0 A4 AD A2. Перевод между данными в кодировке UTF-8 и кодовыми точками Unicode — это трудоемкий процесс (по общему признанию, не большое усилие, и библиотечные функции сделают это за вас). Лучше всего рассматривать «закодированные данные» и «данные Unicode» как отдельные вещи. Вы можете использовать любое удобное для вас представление вплоть до того момента, когда вам нужно (например) отобразить текст на экране. В этот момент вам нужно (перекодировать) его в кодировку, которую понимает ваш выходной адресат.

4

Windows использует UTF-16. Любая кодовая точка в диапазоне от U + 0000 до U + D7FF и от U + E000 до U + FFFF будет сохранена напрямую; любой из этих диапазонов будет разделен на два 16-битных значения в соответствии с правилами кодирования UTF-16.

Например, 0x24B62 будет закодировано как 0xd892,0xdf62.

Вы можете преобразовать строки для работы с ними любым удобным для вас способом, но Windows API все равно захочет и доставит UTF-16, так что это, вероятно, будет наиболее удобным.

3

В стандартном C ++ у нас есть char и wchar_t для хранения символов? char может хранить значения от 0x00 до 0xFF. И wchar_t может хранить значения между 0x0000 и 0xFFFF

Не совсем:

sizeof(char)     == 1   so 1 byte per character.
sizeof(wchar_t)  == ?   Depends on your system
(for unix usually 4 for Windows usually 2).

Символы Юникода занимают до 4 байтов.

Не совсем. Юникод не является кодировкой. Unicode — это стандарт, определяющий, что представляет собой каждая кодовая точка, а кодовые точки ограничены 21 битом. Первые 16 битов определяют положение символа в кодовом элементе, а следующие 5 битов определяют, какой символ используется.

Есть несколько юникодов кодировок (UTF-8, UTF-16 и UTF-32 являются наиболее распространенными) именно так вы храните символы в памяти. Есть практические различия между тремя.

UTF-8: отлично подходит для хранения и транспортировки (так как он компактен)
Плохо, потому что это переменная длина
UTF-16: Ужасный почти во всех отношениях
Он всегда большой и переменной длины
(все, что не на BMP, должно быть закодировано как суррогатные пары)
UTF-32: отлично подходит для представления в памяти, поскольку он имеет фиксированный размер
Плохо, потому что для каждого символа требуется 4 байта, что обычно является избыточным

Лично я использую UTF-8 для транспорта и хранения и UTF-32 для представления текста в памяти.

2

char а также wchar_t не единственные типы данных, используемые для текстовых строк. C ++ 11 представляет новый char16_t а также char32_t типы данных и соответствующие STL std::u16string а также std::u32string typedefs of std::basic_string, чтобы устранить двусмысленность wchar_t Тип, который имеет разные размеры и кодировки на разных платформах. wchar_t 16-битный на некоторых платформах, подходит для кодирования UTF-16, но 32-битный на других платформах, вместо этого подходит для кодирования UTF-32. char16_t конкретно 16-битный и UTF-16, и char32_t это конкретно 32-битный и UTF-32, на всех платформах.

1
По вопросам рекламы [email protected]