Почему в C11 или С++ 11 нет символьного символа ASCII или UTF-8?
Почему нет символа символа UTF-8 в C11 или С++ 11, хотя существуют строковые литералы UTF-8? Я понимаю, что в общем случае символьный литерал представляет собой один символ ASCII, который идентичен однооктевой кодовой точке UTF-8, но ни C, ни С++ не говорят, что кодировка должна быть ASCII.
В принципе, если я прочитаю стандартное право, нет гарантии, что '0'
будет представлять целое число 0x30, но u8"0"
должен представлять последовательность char 0x30 0x00.
EDIT:
Я знаю, что не каждая точка кода UTF-8 поместилась бы в char. Такой литерал был бы полезен только для одноклеточных кодовых точек (aka, ASCII), поэтому я предполагаю, что называть его "символом символов ASCII" будет более подходящим, так что вопрос все еще стоит. Я просто решил задать вопрос с помощью UTF-8, потому что есть строковые литералы UTF-8. Единственный способ, который я могу представить, с точки зрения совместимости с ASCII-значениями, - это написать константу для каждого символа, что было бы не так уж плохо, если бы было только 128, но все же...
Ответы
Ответ 1
Совершенно допустимо писать непортативный C-код, и это является одной из многих веских причин для этого. Не стесняйтесь предполагать, что ваша система использует ASCII или какой-либо их надмножество и предупреждает пользователей о том, что они не должны пытаться запускать вашу программу в системе EBCDIC.
Если вы чувствуете себя очень щедрым, вы можете закодировать чек. Известно, что программа gperf
генерирует код, который включает такую проверку.
_Static_assert('0' == 48, "must be ASCII-compatible");
Или, для компиляторов pre-C11,
extern int must_be_ascii_compatible['0' == 48 ? 1 : -1];
Если вы находитесь на C11, вы можете использовать префикс u
или u
для символьных констант, но не префикс u8
...
/* This is useless, doesn't do what you want... */
_Static_assert(0, "this code is broken everywhere");
if (c == '々') ...
/* This works as long as wchar_t is UTF-16 or UTF-32 or UCS-2... */
/* Note: you shouldn't be using wchar_t, though... */
_Static_assert(__STDC_ISO_10646__, "wchar_t must be some form of Unicode");
if (c == L'々') ...
/* This works as long as char16_t is UTF-16 or UCS-2... */
_Static_assert(__STDC_UTF_16__, "char16_t must be UTF-16");
if (c == u'々') ...
/* This works as long as char32_t is UTF-32... */
_Static_assert(__STDC_UTF_32__, "char32_t must be UTF-32");
if (c == U'々') ...
Существуют некоторые проекты, написанные на очень портативном C и перенесенные в не-ASCII-системы (example). Для этого потребовалось нетривиальное количество усилий по переносу, и нет никаких оснований прилагать усилия, если вы не знаете, что хотите запустить свой код в системах EBCDIC.
О стандартах: Люди, пишущие стандарт C, должны бороться со всеми возможными реализациями C, включая некоторые совершенно странные. Известны системы, в которых sizeof(char) == sizeof(long)
, CHAR_BIT != 8
, интегральные типы имеют ловушечные представления, sizeof(void *) != sizeof(int *)
, sizeof(void *) != sizeof(void (*)())
, va_list
выделены в кучу и т.д. Это кошмар.
Не бейте себя, пытаясь написать код, который будет запускаться в системах, о которых вы даже не слышали, и не пытайтесь найти гарантии в стандарте C.
Например, что касается стандарта C, следующая допустимая реализация malloc
:
void *malloc(void) { return NULL; }
Обратите внимание, что в то время как константы u8"..."
гарантированы как UTF-8, u"..."
и u"..."
не имеют гарантий, кроме того, что кодировка составляет 16 бит и 32 бита на символ соответственно, а фактическая кодировка должна быть документированы реализацией.
Резюме: Безопасно принимать совместимость ASCII в 2012 году.
Ответ 2
Литеральный символ UTF-8 должен иметь переменную длину - для many большинство из них, невозможно хранить одиночный символ в char
или wchar
, какой тип он должен иметь, тогда? Поскольку у нас нет типов переменной длины в C, а не на С++, кроме массивов фиксированных типов размеров, единственным разумным типом для него будет const char *
- и строки C должны быть завершены с нулевой отметкой, поэтому он не будет ничего не меняйте.
Что касается редактирования:
Цитата из стандарта С++ 11:
Глифы для членов базового набора символов источника предназначены для идентификации символов из подмножества ISO/IEC 10646, который соответствует набору символов ASCII. Однако, поскольку сопоставление от исходных символов файла к исходному набору символов (описано в фазе 1 перевода) задано как определено в реализации, требуется реализация, чтобы документировать, как основные исходные символы представлены в исходных файлах.
(сноска к 2.3.1).
Я думаю, что это хорошая причина не гарантировать это. Хотя, как вы отметили в комментарии здесь, для большинства (или каждого) основного компилятора, ASCII-символьные литералы гарантируют реализацию.
Ответ 3
Для С++ это рассмотрено Эволюционная рабочая группа 119: добавление букв символов u8, в разделе Мотивация говорится:
У нас есть пять префиксов кодирования для строковых литералов (none, L, u8, u, U) но только четыре для символьных литералов - недостающий - u8. Если узкий набор символов выполнения не является ASCII, символами символа u8 обеспечит способ записи символьных литералов с гарантированным ASCII кодирование (кодировка с одним кодом u8 - это точно ASCII). Добавление поддержка этих литералов добавит полезную функцию и сделает язык немного более последователен.
EWG обсудила идею добавления символов символа u8 в Rapperswil и приняла изменение. В этом документе представлена формулировка этого расширение.
Это было включено в рабочий проект, используя формулировку N4267: добавление букв символов u8, и мы можем найти формулировку на этом последнем черновик стандарта N4527 и обратите внимание, как раздел 2.14.3
говорят, что они ограничены кодовыми точками, которые вписываются в единый кодовый блок UTF-8:
Литерал символов, начинающийся с u8, например u8'w ', является символом литерал типа char, известный как литерал символа UTF-8. Значение Литерал символа UTF-8 равен его значению кодовой точки ISO10646, при условии, что значение кодовой точки можно представить с помощью одного UTF-8 (то есть, если он является символом US-ASCII). Литеральный символ UTF-8, содержащий несколько c-символов, плохо сформирован.
Ответ 4
Если вы не верите, что ваш компилятор будет обрабатывать '0'
как символ ASCII 0x30, тогда вы можете использовать static_cast<char>(0x30)
.
Ответ 5
Как вам известно, символы с кодировкой UTF-8 нуждаются в нескольких октетах, поэтому char
s, поэтому для них естественным типом является char[]
, который действительно является типом строкового литерала u8
-prefixed! Таким образом, C11 находится на правильном пути, просто придерживаясь его синтаксических соглашений, используя "
для строки, которую нужно использовать в качестве массива char, вместо того, чтобы подразумеваемое семантическое предложение использовать '
вместо.
Примерно "0"
по сравнению с u8"0"
, вы правильно читаете, только последний гарантированно будет идентичен { 0x30, 0 }
даже в системах EBCDIC. Кстати, сам факт, что первый не может быть удобно обработан в вашем коде, если вы обратите внимание на предопределенный идентификатор __STDC_MB_MIGHT_NEQ_WC__
.