14

我希望我的程序尽可能便携。我在字符串中搜索重音字符,例如 è。这会是个问题吗?是否有 C++ 等效的 HTML 实体?

它将在 switch 语句中使用,例如:

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

3 回答 3

11

在 C++ 源代码中使用非 ASCII 字符的主要问题是编译器必须知道用于源代码的编码。如果源是 7 位 ASCII 则通常无关紧要,因为大多数编译器默认假定 ASCII 兼容编码。

此外,并非所有编译器都可以配置编码,因此两个编译器可能会无条件地使用不兼容的编码,这意味着使用非 ASCII 字符可能会导致源代码无法同时使用。

  • GCC:具有用于设置源、执行和广泛执行编码的命令行选项。默认值由语言环境设置,现在通常使用 UTF-8。
  • MSVC:使用所谓的“BOM”来确定源编码(在 UTF-16BE/LE、UTF-8 和系统语言环境编码之间),并且始终使用系统语言环境作为执行编码。编辑:从 VS 2015 Update 2 开始,MSVC 支持编译器开关来控制源和执行字符集,包括对 UTF-8 的支持。看这里
  • Clang:始终使用 UTF-8 作为源和执行编码

因此,如果要搜索的字符串是 UTF-8(可能是因为执行字符集是 UTF-8),请考虑您的代码在搜索重音字符时会发生什么情况。无论字符文字 'é' 是否按预期工作,您都不会找到重音字符,因为重音字符不会由任何单个字节表示。相反,您必须搜索各种字节序列


C++ 允许在字符和字符串文字中使用不同类型的转义。通用字符名称允许您指定一个 Unicode 代码点,并且将按照该字符出现在源代码中的方式进行处理。例如\u00E9\U000000E9

(其他一些语言必须\u支持高达 U+FFFF 的代码点,但缺乏 C++ 对超出此范围的代码点的支持,或者让您使用代理代码点。您不能在 C++ 中使用代理代码点,而是 C++ 具有 \U 变体来直接支持所有代码点.)

UCN 也应该在字符和字符串文字之外工作。在此类文字之外,UCN 仅限于不在基本源字符集中的字符。然而,直到最近编译器还没有实现这个 (C++98) 特性。现在 Clang 似乎有相当完整的支持,MSVC 似乎至少有部分支持,而 GCC 声称提供带有选项的实验性支持-fextended-identifiers

回想一下,UCN 应该与源中出现的实际字符被同等对待;因此,具有良好 UCN 标识符支持的编译器还允许您使用实际字符简单地编写标识符,只要编译器的源编码首先支持该字符。

C++ 还支持十六进制转义。这些是 \x 后跟任意数量的十六进制数字。十六进制转义将表示单个整数值,就好像它是具有该值的单个代码点,并且不会对该值执行到执行字符集的转换。如果您需要表示独立于编码的特定字节(或 char16_t、或 char32_t 或 wchar_t)值,那么这就是您想要的。

还有八进制转义,但它们不如 UCN 或十六进制转义有用。


这是当您在使用 ISO-8859-1 或 cp1252 编码的源文件中使用“é”时 Clang 显示的诊断:

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 可以容纳的范围,因此报告错误。(Clang 也转义了非 ascii 字符,因为它确定运行它的控制台无法处理打印非 ascii 字符)。

于 2012-08-17T15:35:50.073 回答
8

形式上,C++ 甚至在标识符中也支持相当好的 Unicode 子集,因此理论上可以用挪威字符编写标识符,例如antallBlåbærsyltetøyGlass.

实际上,C++ 实现仅支持标识符中的 A 槽 Z、数字 0 到 9 和下划线。一些实现还允许使用美元符号 $。但是,该标准不允许使用美元符号。

要在文本文字中指定 Unicode 字符,您可以使用通用字符 name,它根本不是一个名称,而是更像一个转义序列,例如\u20AC(欧元符号 €)。如果您将源代码保存为 UTF-8,您也可以直接编写此类字符。请注意,Visual C++ 需要 BOM(字节顺序标记)才能识别 UTF-8 源代码。

如果您将字符串视为 UTF-8 编码(即char类型,这在 *nix 中很常见),那么 ASCII 范围 0...127 之外的“é”将不是单个char值,因此不能用作 acase中的标签switch

但是,此特定字符是 Latin-1 的一部分,它是 Windows ANSI Western 的一个子集,它是一种每个字符一个字节的编码。因此,在 Windows 的西方安装中,对字符串值使用 ANSI 编码,它是单个值,可以这样使用。Latin-1 也是 Unicode 的一个子集(包括 Unicode 的前 256 个代码点),因此对于wchar_t基础字符串,例如std::wstring,以及将那些宽字符串作为 Unicode,“é”也是单个值,即与Latin-1 和 Windows ANSI Western。

尽管如此,使用wchar_t表示 Unicode 并不能保证任何任意字符都是单个值。

例如,在 Windows 中,awchar_t仅为 16 位,标准编码为 UTF-16,其中所谓的基本多语言平面(原始 16 位 Unicode)之外的字符由称为代理对的两个值表示。更糟糕的是,即使使用 UTF-32 Unicode 也允许用两个或多个值表示重音字符,即首先是表示基本字符类型的值,然后是通过添加重音符号等来修改它的值,所以为了全面概括,您可以即使使用 32-bit ,也不要依赖字符是单个值wchar_t

于 2012-08-16T22:13:44.980 回答
4

编辑:要在 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;
于 2012-08-16T22:32:59.077 回答