C11 & amp; C ++ 11 расширенный и универсальный экранирование символов

контекст

C11 и C ++ 11 поддерживают расширенные символы в исходных файлах, а также универсальные имена символов (UCN), которые позволяют вводить символы, отсутствующие в базовом наборе исходных символов, используя только символы.

C ++ 11 также определяет несколько этапов трансляции компиляции. В частности, расширенные символы нормализуются к UCN на самом первом этапе перевода, описанном ниже:

§ C ++ 11 2.2p1.1:

Физические символы исходного файла отображаются в
определенным реализацией способом, к основному исходному набору символов
(введение символов новой строки для индикаторов конца строки), если
необходимо. Допустимый набор физических символов исходного файла:
реализации. Триграфные последовательности (2.4) заменены на
соответствующие односимвольные внутренние представления. Любой источник
символ файла, не входящий в базовый исходный набор символов (2.3), заменяется
универсальным именем персонажа, которое обозначает этот символ. (An
реализация может использовать любую внутреннюю кодировку, пока фактическая
расширенный символ, встречающийся в исходном файле, и тот же
расширенный символ, выраженный в исходном файле как
имя универсального символа (т. е. с использованием нотации \ uXXXX)
обрабатывается аналогично, за исключением случаев, когда эта замена возвращается в
необработанный строковый литерал.)


Вопрос

Поэтому мой вопрос:

Соответствует ли стандартная компиляция программы?

#include <stdio.h>

int main(void){
printf("\é\n");
printf("\\u00e9\n");
return 0;
}

потерпеть неудачу, скомпилировать и распечатать

é
é

или скомпилировать и распечатать

\u00e9
\u00e9

, когда бегать?


Информированное личное мнение

Я утверждаю, что ответ заключается в том, что он успешно компилирует и печатает \u00e9поскольку согласно §2.2p1.1 выше мы имеем

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

Из этого следует, что

  • На первом этапе printf("\é\n"); карты для printf("\\u00e9\n");,
  • На этапе 3 Исходный файл разлагается на токены предварительной обработки (§2.2p1.3), из которых Строка литерала "\\u00e9\n" это один.
  • На этапе 5 Каждый элемент исходного набора символов в символьном литерале или строковом литерале, а также каждая escape-последовательность и универсальное имя-символа в символьном литерале или неочищенном строковом литерале преобразуется в соответствующий элемент набора исполняемых символов (§2.2p1.5). Таким образом, по принципу максимального жука, \\ карты для \и фрагмент u00e9 не распознается как UCN и поэтому печатается как есть.

Эксперименты

К сожалению, существующие компиляторы не согласны со мной. Я тестировал как GCC 4.8.2, так и Clang 3.5, и вот что они мне дали:

  • GCC 4.8.2

    $ g++ -std=c++11  -Wall -Wextra ucn.cpp -o ucn
    ucn.cpp: In function 'int main()':
    ucn.cpp:4:9: warning: unknown escape sequence: '\303' [enabled by default]
    printf("\é\n");
    ^
    $ ./ucn
    é
    \u00e9
    
  • Clang 3.5

    $ clang++ -std=c++11  -Wall -Wextra ucn.cpp -o ucn
    ucn.cpp:4:10: warning: unknown escape sequence '\xFFFFFFC3' [-Wunknown-escape-sequence]
    printf("\é\n");
    ^
    ucn.cpp:4:12: warning: illegal character encoding in string literal [-Winvalid-source-encoding]
    printf("\é\n");
    ^
    2 warnings generated.
    $ ./ucn
    é
    \u00e9
    

Я дважды проверил, что é персонаж появляется как C3 A9 с помощью hexdump -C ucn.cppв соответствии с ожидаемой кодировкой UTF-8. Я также подтвердил, что равнина printf("é\n"); или же printf("\u00e9\n"); работает безупречно, так что это не проблема тестируемых компиляторов, которые не могут прочитать исходные файлы UTF-8.

Кто прав?

8

Решение

‘é’ не является допустимым символом для экранирования обратной косой черты в строковом литерале, поэтому обратная косая черта, за которой следует ‘é’ в качестве литерального исходного символа или UCN, должна вызывать диагностику компилятора и неопределенное поведение.

Обратите внимание, однако, что "\\u00e9" не является UCN, которому предшествует обратная косая черта, и что невозможно записать любую последовательность основных исходных символов в строке или символьном литерале, которая является обратной косой чертой, за которой следует UCN. таким образом "\é" а также "\\u00e9" не обязаны вести себя одинаково: поведение "\\u00e9" могут быть совершенно четко определены в то время как поведение "\é" не определено

Если бы мы установили некоторый синтаксис, который позволял бы экранировать обратную косую черту от UCN, скажем, "\«\u00e9»", то это будет иметь неопределенное поведение, как "\é",


  • На первом этапе printf("\é\n"); карты для printf("\\u00e9\n");,

Первый этап преобразования é в UCN не может создать не-UCN, такой как "\\u00e9",


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

$ clang++ -std=c++11  -Wall -Wextra ucn.cpp -o ucn
ucn.cpp:4:10: warning: unknown escape sequence '\é' [-Wunknown-escape-sequence]
printf("\é\n");
^
1 warnings generated.
$ ./ucn
é
\u00e9

Оба компилятора указывают, что их поведение в присутствии неизвестной escape-последовательности заключается в замене escape-последовательности на экранированный символ, поэтому "\é" будет рассматриваться как "é" и программу в целом следует интерпретировать как:

#include <stdio.h>

int main(void){
printf("é\n");
printf("\\u00e9\n");
return 0;
}

Оба компилятора действительно получают это поведение, частично случайно, но также частично, потому что политика для обработки нераспознанных escape-последовательностей, как они делают, является разумным выбором: даже если они видят только нераспознанную escape-последовательность как обратную косую черту, за которой следует байт 0xC3 они удаляют обратную косую черту и оставляют 0xC3 на месте, что означает, что последовательность UTF-8 остается нетронутой для последующей обработки.

2

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

Вы, кажется, смущены, думая, что \\u00e9 это UCN — это не так. UCNs все начинаются с \u, и в вашем случае у вас есть обратный слэш экстракта, который экранирует этот начальный обратный слеш. Так \\u00e9 это последовательность из 6 символов: \, u, 0, 0, e, 9,

редактировать

На первом этапе printf («\ é \ n»); отображается в printf («\ u00e9 \ n») ;.

Вот где вы идете не так — Фаза 1 переводит входные символы в исходные символы, поэтому printf("\é\n"); карты для p r i n t f ( " \ é \ n " ) ;, который так же, как p r i n t f ( " \ \u00e9 \ n " ) ;, но это не то же самое, что printf("\\u00e9\n"); сопоставляется из-за двойной обратной косой черты в последнем. Из-за особой обработки двойной обратной косой черты не существует способа, чтобы обратная косая черта сопровождалась UCN в источнике.

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector