2012-06-07 2 views
17

Почему нет символа символов UTF-8 в C11 или C++ 11, хотя существуют строковые литералы UTF-8? Я понимаю, что в общем случае символьный литерал представляет собой один символ ASCII, который идентичен однооктевой кодовой точке UTF-8, но ни C, ни C++ не говорят, что кодировка должна быть ASCII.Почему в C11 или C++ 11 нет символьного символа ASCII или UTF-8?

В принципе, если я читаю стандартное право, нет никакой гарантии, что '0' будет представлять целое число 0x30, но u8"0" должен представлять последовательность символов 0x30 0x00.

EDIT:

Я знаю, не все UTF-8 код точки будет соответствовать в полукокса. Такой литерал был бы полезен только для одноклеточных кодовых точек (aka, ASCII), поэтому я предполагаю, что называть его «символом символов ASCII» будет более подходящим, так что вопрос все еще стоит. Я просто решил задать вопрос с помощью UTF-8, потому что есть строковые литералы UTF-8. Единственный способ, который я могу представить, с точки зрения гарантии качества ASCII-значений, - это написать константу для каждого символа, что было бы не так уж плохо, если бы было только 128, но все же ...

+5

Поскольку это кодировка с переменной шириной, что бы вы могли ее сохранить? – Pubby

+0

@Pubby: можно сохранить его как 32-битный тип, требуя нулевого заполнения. –

+0

@ Пубби или просто сам int literal. Но мы можем гарантировать строки ASCII * с строкой 'u8 '' 'литералы; почему нет способа гарантировать букву символов ASCII? –

ответ

9

Совершенно допустимо писать непортативный код C, и это является одной из многих веских причин для этого. Не стесняйтесь предполагать, что ваша система использует ASCII или какой-либо их надмножество и предупреждает пользователей о том, что они не должны пытаться запускать вашу программу в системе EBCDIC.

Если вы чувствуете себя очень щедрым, вы можете закодировать чек. Известно, что программа gperf генерирует код, который включает такую ​​проверку.

_Static_assert('0' == 48, "must be ASCII-compatible"); 

Или, предварительно 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), , интегральные типы имеют ловушки, 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 году

+0

Подождите, 'u" ... "' и 'U" ... "' не обязательно должны быть UTF-16 и UTF-32? Я думаю, что 'u8" ... ", тогда это странно. Итак, обратный вопрос! Почему существует 'u8" ... "'? Возможно, я напишу это позже. –

+0

@JoBates Они должны быть массивами 'char16_t' и' char32_t' соответственно. Стандарты просто не вызывали их, например. «Кодированные строки UTF-16», в то время как они упоминают «кодированные строки UTF-8». Имейте в виду, что элементы таких массивов * являются единицами кода Unicode и что C++ 11 Standard предоставляет средства для преобразования в и из того, что он называет «многобайтовые последовательности UTF-16». Я не знаю, что нужно, чтобы быть кодированной строкой UTF-16 или UTF-32 (и, возможно, стандарты тоже не знают), но я знаю, что я могу сделать с 'U" "'. –

+0

@LucDanton Я просто заметил это в стандарте C++ 11 (не в C11): _ «Значение литерала char16_t, содержащего один c-char, равно его кодовой точке кода ISO 10646, при условии, что кодовая точка представляемый с одним 16-битным блоком кода ... Значение литерала char32_t, содержащего один c-char, равно его значению кодовой точки по ISO 10646. »_ Означает ли это, что я мог бы написать что-то вроде' char c = u'0'', гарантируя 'c == 0x30'? Если это так, то я предполагаю, что логика, не включающая листинг ASCII-символа, такая же, как не предоставление явно коротких int-литералов. –

8

Литерал символов UTF-8 должен был бы имеют переменную длину - для много большинство из них, невозможно сохранить одиночный символ в char или wchar, какой тип он должен иметь? Поскольку у нас нет типов переменной длины в C, ни на C++, кроме массивов фиксированных типов размеров, единственным разумным типом для него будет const char * - и строки C должны быть завершены с нулевой отметкой, так что это не будет что-нибудь изменить.

Что касается редактирования:

Цитаты стандарта C++ 11:

глифов для членов базового набора символов источника предназначена для идентификации символов из подмножества ISO/IEC 10646, который соответствует набору символов ASCII. Однако, поскольку сопоставление от исходных символов файла к набору исходных символов (описано в фазе 1 перевода) задано как определено реализацией, для документирования того, как основные исходные символы представлены в исходных файлах, требуется реализация.

(примечание к 2.3.1).

Я думаю, что это хорошая причина не гарантировать его. Хотя, как вы отметили в комментарии здесь, для большинства (или каждого) основного компилятора, ASCII-символьные литералы гарантируются реализацией.

+0

Я понимаю это, но для тех, которые подходят, удобно было бы гарантировать, что вы получите кодировку ASCII/UTF-8, даже если почти каждый (каждый?) компилятор все равно. –

+0

Насколько это действительно полезно? Это было бы полезно, только если вы просто делаете ASCII. –

+0

Подождите. Как насчет 'wchar_t' и' L'0''? Он * является * точно 0x30 0x00 на любом компиляторе. – Forgottn

0

Если вы не верите, что ваш компилятор будет обрабатывать '0' в качестве символа ASCII 0x30, вы можете вместо этого использовать static_cast<char>(0x30).

+2

OP запрашивает рассуждения, а не предложения о том, чтобы реализовать такие гарантии вручную ... – Griwes

+0

@Griwes - это разумный момент - как об этом по какой-то причине: слишком сложно добавить новый синтаксис для чего-то, что вы уже можете сделать (используя static_cast, которое я дал выше, или просто 'char (30)', если вы не хотите набирать столько). –

+0

О, конечно, потому что 'static_cast (0x30)' такой же читаемый, как «0» ... – Griwes

0

Как вы знаете, в UTF-8 кодировке символов требуется несколько октетов, таким образом char с, так что естественный тип для них char[], который действительно, тип для u8 -предоставляемый строковый литерал! Таким образом, C11 находится на правильном пути, просто придерживаясь его синтаксических соглашений, используя " для строки , которая должна использоваться как массив символов, а не подразумеваемое предложение на основе семантики вместо '.

О "0" по сравнению с u8"0", вы читаете справа, только последний гарантированно идентичен { 0x30, 0 }, даже в системах EBCDIC. Кстати, сам факт, что первый не может быть удобно использован в вашем коде, если вы обратите внимание на предопределенный идентификатор __STDC_MB_MIGHT_NEQ_WC__.

7

Для C++ это было адресовано по Evolution Working Group issue 119: Adding u8 character literals которого Мотивация раздел говорит:

У нас есть пять кодированию префиксы для строковых литералов (отсутствует, L, u8, U, U) , но только четыре для символьных литералов - недостающий - u8.Если набор символов с ограниченным исполнением не является ASCII, символьные символы u8 предоставят способ записи символьных литералов с гарантированной кодировкой ASCII (кодировка U8 с одним кодовым кодированием - это точно ASCII). Добавление поддержки для этих литералов добавит полезную функцию и сделает язык несколько более последовательным.

EWG обсудила идею добавления символов символов u8 в Rapperswil и приняла изменение. Настоящий документ содержит формулировку для этого расширения .

Это был включен в рабочий проект, используя формулировку из N4267: Adding u8 character literals и мы можем найти формулировку в это время последний проект стандарта N4527 и обратите внимание, как раздел 2.14.3 говорят, что они ограничиваются точками кода, которые помещаются в один UTF -8-код единицы:

Символьный литерал, который начинается с u8, такие как u8'w», является символом литерал типа полукокса, известный как символ UTF-8 буквального. Значение символьный символ UTF-8 равно его значению кодовой точки ISO10646, при условии, что значение кодовой точки может быть представлено с помощью одного блока кода UTF-8 (то есть, если это символ US-ASCII) , A Символьный символ UTF-8, содержащий несколько c-символов, плохо сформирован.

Смежные вопросы