我希望我的程序尽可能便携。我在字符串中搜索重音字符,例如 è。这会是个问题吗?是否有 C++ 等效的 HTML 实体?
它将在 switch 语句中使用,例如:
switch(someChar) //someChar is of type char
{
case 'é' :
x = 1;
break;
case 'è' :
...
}
我希望我的程序尽可能便携。我在字符串中搜索重音字符,例如 è。这会是个问题吗?是否有 C++ 等效的 HTML 实体?
它将在 switch 语句中使用,例如:
switch(someChar) //someChar is of type char
{
case 'é' :
x = 1;
break;
case 'è' :
...
}
在 C++ 源代码中使用非 ASCII 字符的主要问题是编译器必须知道用于源代码的编码。如果源是 7 位 ASCII 则通常无关紧要,因为大多数编译器默认假定 ASCII 兼容编码。
此外,并非所有编译器都可以配置编码,因此两个编译器可能会无条件地使用不兼容的编码,这意味着使用非 ASCII 字符可能会导致源代码无法同时使用。
因此,如果要搜索的字符串是 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 字符)。
形式上,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
。
编辑:要在 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;