Нехорошо ли иметь акцентированные символы в исходном коде С++?

Я хочу, чтобы моя программа была как можно более переносимой. Я ищу строку для акцентированных символов, например. è. Это может быть проблема? Существует ли эквивалент С++ для HTML-объектов?

Он будет использоваться в инструкции switch, например:

switch(someChar) //someChar is of type char
{
   case 'é' :
        x = 1;
        break;
   case 'è' :
   ...
}

Ответы

Ответ 1

Основная проблема с использованием символов, отличных от ASCII в источнике С++, заключается в том, что компилятор должен знать кодировку, используемую для источника. Если источником является 7-разрядный ASCII, это обычно не имеет значения, так как большинство всех компиляторов по умолчанию используют кодировку, совместимую с ASCII.

Также не все компиляторы конфигурируются относительно кодировки, поэтому два компилятора могут безоговорочно использовать несовместимые кодировки, а это означает, что использование символов, отличных от ASCII, может привести к исходному коду, который нельзя использовать с обоими.

  • GCC: имеет параметры командной строки для установки кодировки источника, исполнения и широкого исполнения. По умолчанию устанавливаются локаль, которая обычно использует UTF-8 в эти дни.
  • MSVC: использует так называемую "спецификацию" для определения исходной кодировки (между UTF-16BE/LE, UTF-8 и кодировкой языковой системы) и всегда использует языковой стандарт системы в качестве исполнения. edit: Начиная с версии VS 2015 Update 2, MSVC поддерживает компиляторы для управления кодировками источника и исполнения, включая поддержку UTF-8. см. здесь
  • Clang: всегда использует UTF-8 в качестве кодировки источника и исполнения

Итак, подумайте, что происходит с вашим кодом для поиска акцентированного символа, если искомая строка является UTF-8 (возможно, потому, что набор символов выполнения - UTF-8). Является ли символьный литерал "é" работает так, как вы ожидаете, или нет, вы не будете находить акцентированные символы, потому что акцентированные символы не будут представлены ни одним байтом. Вместо этого вам придется искать различные байтовые последовательности.


Существуют разные типы экранов, которые С++ допускает в символьных и строковых литералах. Универсальные имена символов позволяют назначать кодовую точку Юникода и обрабатываться точно так, как если бы этот символ появился в источнике. Например \u00E9 или \U000000E9.

(некоторые другие языки имеют \u для поддержки кодовых точек до U + FFFF, но не имеют поддержки С++ для кодовых точек, выходящих за рамки этого, или для использования суррогатных кодовых точек. Вы не можете использовать суррогатные коды в С++, а вместо этого С++ имеет вариант \U для поддержки всех кодовых точек непосредственно.)

UCN также должны работать вне символов и строковых литералов. Вне таких литералов UCN ограничиваются символами, не содержащими базовый набор символов. Однако до недавнего времени компиляторы не реализовали эту функцию (С++ 98). Теперь у Clang появилась довольно полная поддержка, MSVC, похоже, имеет хотя бы частичную поддержку, и GCC хочет предоставить экспериментальную поддержку с опцией -fextended-identifiers.

Напомним, что UCN должны рассматриваться одинаково с фактическим символом, появляющимся в источнике; Таким образом, компиляторы с хорошей поддержкой идентификатора UCN также позволяют просто писать идентификаторы с использованием фактического символа, пока кодирование исходного кода компилятора поддерживает символ в первую очередь.

С++ также поддерживает шестнадцатеричные экраны. Это \x, за которым следует любое число шестнадцатеричных цифр. Выражение hex будет представлять собой единое целочисленное значение, как если бы это был единственный код с этим значением, и никакое преобразование в кодировку выполнения не выполняется по значению. Если вам нужно представить конкретное значение байта (или char16_t или char32_t или wchar_t), не зависящее от кодировок, то это то, что вы хотите.

Есть также восьмеричные escape-последовательности, но они не так часто полезны, как UCN или шестнадцатеричные escape-последовательности.


Здесь диагноз, который показывает 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 и что эта кодовая точка находится за пределами диапазона, который может содержать один char, и поэтому сообщает об ошибке. (Кланг также избегает символа не ascii, потому что он определил, что консоль, в которой он запускался, не может обрабатывать печать символа, отличного от ascii).

Ответ 2

Формально С++ поддерживает довольно хороший подмножество Unicode даже в идентификаторах, поэтому теоретически можно писать идентификаторы, например. Норвежские символы, такие как antallBlåbærsyltetøyGlass.

На практике реализация С++ поддерживает только A-Z Z, цифры от 0 до 9 и подчеркивание в идентификаторах. Некоторые реализации также позволяют знаку доллара $. Однако стандарт не позволяет знака доллара.

Чтобы указать символ Юникода в текстовом литерале, вы можете использовать универсальное имя символа, которое не является именем вообще, но скорее похожее на escape-последовательность, например. \u20AC (знак евро). Вы также можете писать такие символы напрямую, если сохранить исходный код как UTF-8. Обратите внимание, что Visual С++ требует спецификацию (байтовый порядок) для распознавания исходного кода UTF-8.

Если вы обрабатываете строки как кодированные UTF-8 (т.е. тип char, как это принято в * nix), то "é", который находится за пределами диапазона ASCII 0... 127, не будет ни одним char и, следовательно, не может использоваться как метка case в switch.

Однако этот конкретный символ является частью латинского-1, который является подмножеством Windows ANSI Western, который представляет собой кодировку с одним байтом на символ. Поэтому в западной установке Windows, использующей кодировку ANSI для строковых значений, это одно значение и может быть использовано так. Latin-1 также является подмножеством Unicode (содержит первые 256 кодовых точек Unicode), поэтому с строками на основе wchar_t, например. std::wstring, и с такими широкими строками, как Unicode, "é" также является одним значением, а именно тем же значением, что и в латинском-1 и в Windows ANSI Western.

Тем не менее использование wchar_t для представления Unicode не гарантирует, что любой произвольный символ будет единственным значением.

Например, в Windows a wchar_t составляет всего 16 бит, а стандартная кодировка - UTF-16, где символы вне так называемой Basic Multilingual Plane (исходный 16-разрядный Unicode) представлены двумя значениями, называемыми суррогатными пара. Хуже того, даже с Unicode UTF-32 для акцентированных символов представлены два или более значений, а именно сначала значение, представляющее вид базового символа, а затем значения, которые изменяют его, добавляя знаки акцента и т.д., Поэтому для полной общности вы можете не полагайтесь на символы, являющиеся одиночными значениями, даже с 32-битным wchar_t.

Ответ 3

Изменить: Чтобы использовать макрос в операторе 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;