Плохо ли иметь акцентированные символы в исходном коде c++?
Я хочу, чтобы моя программа была максимально переносимой. Я ищу строку для акцентированных символов, например è. Это может быть проблемой? Существует ли эквивалент HTML-сущностей на C++?
Он будет использоваться в операторе switch, например:
switch(someChar) //someChar is of type char
{
case 'é' :
x = 1;
break;
case 'è' :
...
}
3 ответов
главной проблемой используя не-ASCII символов в C++ код, что компилятор должен быть в курсе кодирования для источника. Если источником является 7-битный ASCII, то это обычно не имеет значения, так как большинство компиляторов по умолчанию принимают кодировку, совместимую с ASCII.
также не все компиляторы настраиваются относительно кодировки, поэтому два компилятора могут безоговорочно использовать несовместимые кодировки, что означает, что использование символов, отличных от ASCII, может привести к исходному коду, который не может быть использовать обе.
- GCC: имеет параметры командной строки для установки источника, выполнения и широких кодировок выполнения. По умолчанию установлен язык, который обычно использует UTF-8 в эти дни.
- MSVC: использует так называемую " BOM " для определения исходной кодировки (между UTF-16BE/LE, UTF-8 и кодировкой системного языка) и всегда использует системный язык в качестве кодировки выполнения. edit: начиная с VS 2015 Update 2, MSVC поддерживает переключатели компилятора для управления источником и charsets исполнения, включая поддержку UTF-8. посмотреть здесь
- Clang: всегда использует UTF-8 в качестве исходных и исполнительных кодировок
поэтому рассмотрите, что происходит с вашим кодом для поиска акцентированного символа, если искомая строка-UTF-8 (возможно, потому, что набор символов выполнения-UTF-8). Независимо от того, работает ли буквальный символ "é" так, как вы ожидаете, вы не найдете акцентированных символов, потому что акцентированные персонажи не будут представлены ни одним байтом. Вместо этого вам придется искать различные байт последовательности.
существуют различные виды экранирования, которые C++ допускает в символьных и строковых литералах. Универсальные имена символов позволяют назначить кодовую точку Юникода и будут обрабатываться точно так же, как если бы этот символ появился в источнике. Например \u00E9
или \U000000E9
.
(другие языки \u
для поддержки кодовые точки до U+FFFF, но не поддерживают C++для кодовых точек за пределами этого или заставляют вас использовать суррогатные кодовые точки. Вы не можете использовать суррогатные кодовые точки в C++ , и вместо этого C++ имеет вариант \U для поддержки всех кодовых точек напрямую.)
UCNs также должны работать вне символьных и строковых литералов. Вне таких литералов UCNs ограничены символами, не входящими в основной исходный набор символов. Однако до недавнего времени компиляторы не реализовывали эту функцию (C++98). Теперь Clang, похоже, имеет довольно полную поддержку, MSVC, похоже, имеет хотя бы частичную поддержку, а GCC стремится предоставить экспериментальную поддержку с опцией -fextended-identifiers
.
Напомним, что UCNs должны рассматриваться одинаково с фактическим символом, появляющимся в источнике; таким образом, компиляторы с хорошей поддержкой идентификаторов UCN также позволяют просто писать идентификаторы, используя фактический символ, пока исходная кодировка компилятора поддерживает символ в первом место.
C++ также поддерживает hex escapes. Это \x, за которым следует любое количество шестнадцатеричных цифр. Шестнадцатеричный escape будет представлять собой одно целое значение, как если бы это была одна кодовая точка с этим значением, и никакое преобразование в кодировку выполнения не выполняется для значения. Если вам нужно представить определенное значение байта (или char16_t, или char32_t, или wchar_t) независимо от кодировок, то это то, что вы хотите.
есть также восьмеричные побеги, но они не такие, как обычно полезно, как ucns или hex убегает.
вот диагноз, который Clang показывает, когда вы используете "é" в исходном файле, закодированном ISO-8859-1 или cp1252:
warning: illegal character encoding in character literal [-Winvalid-source-encoding]
std::printf("%c\n",'<E9>');
^
Clang выдает это только как предупреждение и просто выводит объект char со значением исходного байта. Это делается для обратной совместимости с исходным кодом, отличным от UTF-8.
если вы используете кодированный источник UTF-8, вы получите следующее:
error: character too large for enclosing character literal type
std::printf("%c\n",'<U+00E9>');
^
Clang обнаруживает, что Кодировка UTF-8 соответствует кодовой точке Unicode U + 00E9, и эта кодовая точка находится вне диапазона, который может содержать один символ, и поэтому сообщает об ошибке. (Лязг избегает не-ASCII символы, так как он определил, что консоли это выполняться под не мог справиться печать символов ASCII).
формально C++ поддерживает довольно хорошее подмножество Unicode даже в идентификаторах, поэтому теоретически можно писать идентификаторы, например, норвежскими символами, такими как antallBlåbærsyltetøyGlass
.
на практике реализации C++ поддерживают только корыто Z, цифры от 0 до 9 и подчеркивание в идентификаторах. Некоторые реализации также позволяют знак доллара $. Однако стандарт не допускает знака доллара.
чтобы указать символ Юникода в текстовом литерале, можно использовать универсальное имя персонажа, который не является именем вообще, но больше похож на escape-последовательность, например \u20AC
(знак евро €). Вы также можете написать такие символы напрямую, если сохраните исходный код как UTF-8. Обратите внимание, что Visual C++ требует спецификации (метки порядка байтов) для распознавания исходного кода UTF-8 как такового.
если вы рассматриваете строки как кодированные UTF-8 (т. е. char
type, как обычно в *nix), то "é", который находится вне диапазона ASCII 0...127, не будет ни одного char
значением, и таким образом не может использоваться как case
метка switch
.
однако этот конкретный символ является частью латинского-1, который является подмножеством Windows ANSI Western, который является кодировкой один байт на символ. Таким образом, в Западной установке Windows, используя кодировку ANSI для строковых значений, это одно значение и может быть использовано. Латинский-1 также является подмножеством Unicode (содержащим первые 256 кодовых точек Unicode), поэтому с wchar_t
строк , например,std::wstring
, и с этими широкими строками как Unicode, " é " также является одним значением, а именно тем же значением, что и в Latin-1 и в Windows ANSI Western.
все-таки, используя wchar_t
для представления Unicode нет гарантии, что любой произвольный символ будет одним значением.
например, в Windows a wchar_t
- это всего лишь 16 бит, а стандартная кодировка-UTF-16, где символы вне так называемого Основной Многоязычный Самолет (исходный 16-битный Unicode) представлены двумя значениями, называемыми a суррогатная пара. Хуже того, даже с UTF-32 Unicode позволяет представлять акцентированные символы с двумя или более значениями, А именно сначала значение, представляющее вид базового символа, а затем значения, которые изменяют его, добавляя знаки акцента и т. д., поэтому для полной общности вы не можете полагаться на символы, являющиеся одиночными значениями даже с 32-битным wchar_t
.
Edit: для использования макроса в инструкции switch требуется два изменения в моем исходном решении. Сначала каждый символ должен соответствовать целому типу; лучший способ обеспечить это-использовать широкие символы с wchar_t
. Во-вторых, макрос должен быть символьным литералом вместо строкового литерала. Г. Е.
#define E_GRAVE L'\u00E8'
wchar_t someChar = ...;
switch(someChar)
{
case E_GRAVE :
x = 1;
break;
...
}
Один из полностью переносимых способов-определить макросы для акцентированных символов и полагаться на конкатенацию строк.
// è (U+00E8) in UTF-8 encoding
#define E_GRAVE "\xC3\xA8"
cout << "Resum" E_GRAVE << endl;
это курс предполагает, что вы работаете с UTF-8. Таким образом, вы можете поддерживать любой набор символов. Вот как вы это сделаете в Windows с UTF-16:
#define E_GRAVE L"\u00E8"
wchar_t * resume = L"Resum" E_GRAVE;