Обратите внимание, что речь идет не о «строгом программировании в Юникоде» как таковом, а о некотором практическом опыте.
В моей компании мы создали библиотеку-оболочку для библиотеки IBM ICU. Библиотека-оболочка имеет интерфейс UTF-8 и преобразуется в UTF-16, когда необходимо вызвать ICU. В нашем случае мы не особо беспокоились о падении производительности. Когда производительность была проблемой, мы также предоставляли интерфейсы UTF-16 (с использованием нашего собственного типа данных).
Приложения могут оставаться в основном как есть (с использованием char), хотя в некоторых случаях им необходимо знать об определенных проблемах. Например, вместо strncpy () мы используем оболочку, которая избегает обрезания последовательностей UTF-8. В нашем случае этого достаточно, но можно также рассмотреть проверки на объединение символов. У нас также есть обертки для подсчета количества кодовых точек, количества графем и т. Д.
При взаимодействии с другими системами нам иногда требуется настраивать композицию персонажей, поэтому вам может потребоваться некоторая гибкость (в зависимости от вашего приложения).
Мы не используем wchar_t. Использование ICU позволяет избежать неожиданных проблем с переносимостью (но, конечно, не других неожиданных проблем :-).
strncpy
при правильном использовании совершенно безопасно использовать с UTF-8.strcpy
(что действительно безопасно для использования с UTF-8). Люди, использующие,strncpy
вероятно, делают это, потому что они не знают, достаточно ли большой целевой буфер, поэтому они хотят передать максимальное количество байтов для копирования, что действительно может создать недопустимые последовательности UTF-8.C99 или ранее
Стандарт C (C99) предусматривает использование широких символов и многобайтовых символов, но поскольку нет гарантии, что эти широкие символы могут содержать, их значение несколько ограничено. Для данной реализации они обеспечивают полезную поддержку, но если ваш код должен иметь возможность перемещаться между реализациями, нет достаточной гарантии, что они будут полезны.
Следовательно, подход, предложенный Хансом ван Экком (который заключается в написании оболочки для библиотеки ICU - International Components for Unicode), является правильным, IMO.
Кодировка UTF-8 имеет множество достоинств, одно из которых заключается в том, что если вы не вмешиваетесь в данные (например, усекая их), то их можно скопировать функциями, которые не полностью осведомлены о тонкостях UTF-8. кодирование. Это категорически не относится к
wchar_t
.Unicode полностью - это 21-битный формат. То есть Unicode резервирует кодовые точки от U + 0000 до U + 10FFFF.
Одна из полезных особенностей форматов UTF-8, UTF-16 и UTF-32 (где UTF означает формат преобразования Unicode - см. Unicode ) заключается в том, что вы можете конвертировать между тремя представлениями без потери информации. Каждый может представлять все, что могут представлять другие. И UTF-8, и UTF-16 являются многобайтовыми форматами.
UTF-8 хорошо известен как многобайтовый формат с тщательно продуманной структурой, которая позволяет надежно находить начало символов в строке, начиная с любой точки строки. У однобайтовых символов старший бит установлен в ноль. Многобайтовые символы имеют первый символ, начинающийся с одного из битовых шаблонов 110, 1110 или 11110 (для 2-байтовых, 3-байтовых или 4-байтовых символов), а последующие байты всегда начинаются с 10. Символы продолжения всегда находятся в диапазон 0x80 .. 0xBF. Существуют правила, согласно которым символы UTF-8 должны быть представлены в минимально возможном формате. Одним из следствий этих правил является то, что байты 0xC0 и 0xC1 (также 0xF5..0xFF) не могут появляться в действительных данных UTF-8.
U+0000 .. U+007F 1 byte 0xxx xxxx U+0080 .. U+07FF 2 bytes 110x xxxx 10xx xxxx U+0800 .. U+FFFF 3 bytes 1110 xxxx 10xx xxxx 10xx xxxx U+10000 .. U+10FFFF 4 bytes 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
Первоначально предполагалось, что Unicode будет 16-битным кодовым набором, и все будет помещено в 16-битное кодовое пространство. К сожалению, реальный мир более сложен, и его пришлось расширить до нынешней 21-битной кодировки.
UTF-16, таким образом, представляет собой единый кодовый блок (16-битное слово), установленный для «Базовой многоязычной плоскости», то есть символы с кодовыми точками Unicode U + 0000 .. U + FFFF, но использует две единицы (32-битные) для символы вне этого диапазона. Таким образом, код, работающий с кодировкой UTF-16, должен иметь возможность обрабатывать кодировки переменной ширины, как и UTF-8. Коды для двухзначных символов называются суррогатами.
UTF-32, конечно, может кодировать любую кодовую точку Unicode в единой единице хранения. Он эффективен для вычислений, но не для хранения.
Вы можете найти гораздо больше информации на сайтах ICU и Unicode.
C11 и
<uchar.h>
Стандарт C11 изменил правила, но даже сейчас (середина 2017 года) не все реализации учли эти изменения. Стандарт C11 суммирует изменения для поддержки Unicode следующим образом:
Далее следует лишь минимальный набросок функциональности. В спецификацию входят:
(Перевод перекрестных ссылок:
<stddef.h>
определяетsize_t
,<wchar.h>
определяетmbstate_t
и<stdint.h>
определяетuint_least16_t
иuint_least32_t
.)<uchar.h>
Заголовок также определяет минимальный набор (перезапускаемых) функций преобразования:Существуют правила относительно того, какие символы Unicode могут использоваться в идентификаторах с использованием нотации
\unnnn
или\U00nnnnnn
. Возможно, вам придется активно активировать поддержку таких символов в идентификаторах. Например, GCC требует-fextended-identifiers
разрешить это в идентификаторах.Обратите внимание, что macOS Sierra (10.12.5), если назвать только одну платформу, не поддерживает
<uchar.h>
.источник
wchar_t
и друзьям здесь немного не хватает. Эти типы необходимы для того, чтобы библиотека C могла обрабатывать текст в любой кодировке (включая кодировки, отличные от Unicode). Без широких символьных типов и функций библиотеке C потребовался бы набор функций обработки текста для каждой поддерживаемой кодировки: представьте, что у вас есть koi8len, koi8tok, koi8printf только для текста в кодировке KOI-8 и utf8len, utf8tok, utf8printf для UTF-8. текст. Вместо этого, нам повезло иметь только один набор этих функций (не считая первоначально одни ASCII):wcslen
,wcstok
, иwprintf
.mbstowcs
и их друзей) для преобразования любой поддерживаемой кодировки вwchar_t
. Послеwchar_t
форматирования программист может использовать единый набор широких функций обработки текста, которые предоставляет библиотека C. Хорошая реализация библиотеки C будет поддерживать практически любую кодировку, которая когда-либо понадобится большинству программистов (в одной из моих систем у меня есть доступ к 221 уникальной кодировке).wchar_t
достаточно широкой, чтобы содержать любой символ, поддерживаемый реализацией. Это означает (возможно, с одним заметным исключением) большинство реализаций будут гарантировать, что они достаточно широки, чтобы используемая программа моглаwchar_t
обрабатывать любую кодировку, поддерживаемую системой (wchar_t
ширина Microsoft составляет всего 16 бит, что означает, что их реализация не полностью поддерживает все кодировки, прежде всего различные кодировки UTF, но они являются исключением, а не правилом).Этот FAQ содержит большое количество информации. Между этой страницей и этой статьей Джоэла Спольски у вас будет хорошее начало.
Один вывод, к которому я пришел по пути:
wchar_t
- это 16 бит в Windows, но не обязательно 16 бит на других платформах. Я думаю, что это необходимое зло для Windows, но, вероятно, его можно избежать в другом месте. Причина, по которой это важно в Windows, заключается в том, что вам нужно использовать файлы, в имени которых есть символы, отличные от ASCII (вместе с версией функций W).Обратите внимание, что API-интерфейсы Windows, принимающие
wchar_t
строки, ожидают кодировки UTF-16. Также обратите внимание, что это отличается от UCS-2. Обратите внимание на суррогатные пары. Эта тестовая страница содержит полезные тесты.Если вы программируете на Windows, вы не можете использовать
fopen()
,fread()
,fwrite()
и т.д. , так как они только принимаютchar *
и не понимают кодировку UTF-8. Делает переносимость болезненной.источник
f*
и друзья работают сchar *
на каждой платформе , потому что стандарт говорит так - использоватьwcs*
вместо этого для wchar_t.Чтобы выполнить строгое программирование Unicode:
strlen
,strcpy
... но их WideString коллегиwstrlen
,wsstrcpy
...)Многобайтовые последовательности символов - это кодировка, которая предшествует кодировке UTF-16 (обычно используемой
wchar_t
), и мне кажется, что она скорее предназначена только для Windows.Я никогда не слышал
wint_t
.источник
Самое главное - всегда четко различать текстовые и двоичные данные . Попробуйте следовать модели Python 3.x
str
vs.bytes
или SQLTEXT
vsBLOB
..К сожалению, C сбивает с толку, используя
char
как «символ ASCII», так иint_least8_t
. Вы захотите сделать что-то вроде:typedef char UTF8; // for code units of UTF-8 strings typedef unsigned char BYTE; // for binary data
Вам могут понадобиться typedef для кодовых единиц UTF-16 и UTF-32, но это более сложно, потому что кодировка
wchar_t
не определена. Вам понадобится только препроцессор#if
. Вот некоторые полезные макросы в C и C ++ 0x:__STDC_UTF_16__
- Если определено, тип_Char16_t
существует и является UTF-16.__STDC_UTF_32__
- Если определено, тип_Char32_t
существует и является UTF-32.__STDC_ISO_10646__
- Если определено, тоwchar_t
используется UTF-32._WIN32
- В Windowswchar_t
используется UTF-16, даже если это нарушает стандарт.WCHAR_MAX
- Может использоваться для определения размераwchar_t
, но не для определения того , использует ли ОС его для представления Unicode.Смотрите также:
Нет. UTF-8 - это вполне допустимая кодировка Unicode, в которой используются
char*
строки. Его преимущество заключается в том, что если ваша программа прозрачна для байтов, отличных от ASCII (например, конвертер окончания строки, который действует на другие символы\r
и\n
пропускает их через другие символы без изменений), вам нужно вообще не вносить никаких изменений!Если вы выберете UTF-8, вам нужно будет изменить все предположения, что
char
символ = (например, не вызыватьtoupper
в цикле) илиchar
столбец = screen (например, для переноса текста).Если вы выберете UTF-32, у вас будет простота символов фиксированной ширины (но не графем фиксированной ширины). , но вам нужно будет изменить тип всех ваших строк).
Если вы выберете UTF-16, вам придется отказаться от использования символов фиксированной ширины и от предположения о 8-битных единицах кода, что делает этот путь наиболее сложным путем обновления однобайтовых кодировок.
Я бы рекомендовал активно избегать,
wchar_t
потому что это не кросс-платформенный: иногда это UTF-32, иногда это UTF-16, а иногда это восточноазиатская кодировка до Unicode. Я бы рекомендовал использоватьtypedefs
Еще важнее избегать
TCHAR
.источник
char *
могут иметь проблемы, если они переданыconst char *
последним, о котором я помню (но я не уверен в этом и о том, какие функции, поэтому относитесь к этому с щепоткой соли). Тот факт, что с другими языками сложнее, не означает, что это плохой дизайн.Я бы не стал доверять любой стандартной реализации библиотеки. Просто используйте свои собственные типы юникода.
#include <windows.h> typedef unsigned char utf8_t; typedef unsigned short utf16_t; typedef unsigned long utf32_t; int main ( int argc, char *argv[] ) { int msgBoxId; utf16_t lpText[] = { 0x03B1, 0x0009, 0x03B2, 0x0009, 0x03B3, 0x0009, 0x03B4, 0x0000 }; utf16_t lpCaption[] = L"Greek Characters"; unsigned int uType = MB_OK; msgBoxId = MessageBoxW( NULL, lpText, lpCaption, uType ); return 0; }
источник
В основном вы хотите иметь дело со строками в памяти как с
wchar_t
массивами, а не с символами. Когда вы выполняете какой-либо ввод-вывод (например, чтение / запись файлов), вы можете кодировать / декодировать с помощью UTF-8 (это, вероятно, наиболее распространенная кодировка), которую достаточно просто реализовать. Просто погуглите RFC. Так что в памяти ничего не должно быть многобайтовым. Одинwchar_t
представляет одного персонажа. Однако когда вы переходите к сериализации, вам нужно кодировать что-то вроде UTF-8, где некоторые символы представлены несколькими байтами.Вам также придется писать новые версии
strcmp
и т. Д. Для строк широких символов, но это не большая проблема. Самая большая проблема будет связана с взаимодействием с библиотеками / существующим кодом, которые принимают только массивы символов.А когда дело доходит до
sizeof(wchar_t)
(вам потребуется 4 байта, если вы хотите сделать это правильно), вы всегда можете переопределить его на больший размер с помощьюtypedef
/macro
hacks, если вам нужно.источник
Насколько я знаю, wchar_t зависит от реализации (как видно из этой статьи в вики ). И это не юникод.
источник