Сначала вам нужно лучше понять Unicode. Конкретные ответы на ваши вопросы находятся внизу.
Концепции
Вам нужен более тонкий набор концепций, чем требуется для очень простой обработки текста, как преподают на вводных курсах по программированию.
- байт
- кодовая единица
- кодовая точка
- абстрактный персонаж
- воспринимаемый пользователем персонаж
Байт — это наименьшая адресуемая единица памяти. Сегодня обычно 8 бит, способных хранить до 256 различных значений. По определению char — это один байт.
Единица кода — это наименьшая единица данных фиксированного размера, используемая для хранения текста. Когда вам на самом деле не важно содержание текста, и вы просто хотите скопировать его куда-нибудь или подсчитать, сколько памяти использует текст, тогда вам важны единицы кода. В противном случае единицы кода бесполезны.
Кодовая точка представляет отдельный член набора символов. Какие бы «символы» ни находились в наборе символов, всем им присваивается уникальный номер, и всякий раз, когда вы видите закодированное конкретное число, вы знаете, с каким членом набора символов вы имеете дело.
Абстрактный символ — это сущность, имеющая значение в лингвистической системе и отличная от своего представления или любых кодовых точек, присвоенных этому значению.
Персонажи воспринимаются пользователями так, как они звучат; то, что пользователь считает символом в любой лингвистической системе, которую он использует.
В прежние времена char
представляло все эти вещи: char
по определению является байтом, в char*
строках единицами кода являются char
s, наборы символов были небольшими, поэтому 256 значений, представляемых char
, было достаточно для представления каждого члена, а Поддерживаемые лингвистические системы были простыми, поэтому члены наборов символов в основном представляли символы, которые пользователи хотели использовать напрямую.
Но этой простой системы с char
, представляющей почти все, было недостаточно для поддержки более сложных систем.
Первая проблема, с которой столкнулись, заключалась в том, что некоторые языки используют гораздо больше, чем 256 символов. Так были введены «широкие» символы. Широкие символы по-прежнему использовали один тип для представления четырех из вышеперечисленных понятий, кодовых единиц, кодовых точек, абстрактных символов и символов, воспринимаемых пользователем. Однако широкие символы больше не являются одиночными байтами. Считалось, что это самый простой метод поддержки больших наборов символов.
Код может быть в основном таким же, за исключением того, что он будет работать с широкими символами вместо char
.
Однако оказывается, что многие лингвистические системы не так просты. В некоторых системах имеет смысл не обязательно представлять каждый воспринимаемый пользователем символ одним абстрактным символом в наборе символов. В результате текст, использующий набор символов 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
. Кодовая точка Юникода U+24B62 аналогичным образом кодируется четырьмя кодовыми единицами 0xF0 0xA4 0xAD 0xA2
.
Таким образом, вы можете использовать std::string
для хранения данных в кодировке UTF-8.
В Windows main()
поддерживает не только ASCII, но и любую кодировку системы char
. К сожалению, Windows не поддерживает UTF-8 как системную кодировку char
, как это делают другие платформы, поэтому вы ограничены устаревшими кодировками, такими как cp1252 или любой другой, на использование которой настроена ваша система. Однако вы можете использовать вызов API Win32 для прямого доступа к параметрам командной строки UTF-16 вместо использования параметров main()
s argc
и argv
. См. GetCommandLineW()
и < a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx" rel="noreferrer">CommandLineToArgvW
.
Параметр argv
wmain()
полностью поддерживает Unicode. 16-битные кодовые единицы, хранящиеся в wchar_t
в Windows, являются кодовыми единицами UTF-16. Windows API изначально использует кодировку UTF-16, поэтому с ней довольно легко работать в Windows. wmain()
не является стандартным, поэтому полагаться на него нельзя.
person
bames53
schedule
28.09.2012