在c ++源代码中加入重音字符是不是很糟糕?

时间:2012-08-16 22:07:05

标签: c++ unicode portability

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

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

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

3 个答案:

答案 0 :(得分:11)

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

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

  • GCC:具有用于设置源,执行和广泛执行编码的命令行选项。默认值由区域设置设置,这些日期通常使用UTF-8。
  • MSVC:使用所谓的“BOM”来确定源编码(在UTF-16BE / LE,UTF-8和系统区域设置编码之间),并始终使用系统区域设置作为执行编码。 编辑:从VS 2015 Update 2开始,MSVC支持编译器开关来控制源和执行字符集,包括对UTF-8的支持。 see here
  • 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或十六进制逃脱那样常用。


以下是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,并且此代码点超出了单个字符可以容纳的范围,因此报告错误。 (Clang也逃脱了非ascii字符,因为它确定它运行的控制台无法处理打印非ascii字符)。

答案 1 :(得分:8)

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

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

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

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

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

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

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

答案 2 :(得分:4)

编辑:要在switch语句中使用宏,需要对原始解决方案进行两次更改。首先,每个角色必须符合整体类型;确保这一点的最佳方法是使用wchar_t的宽字符。其次,宏必须是字符文字而不是字符串文字。 E.G。

#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。您可以通过这种方式支持任何字符集。以下是使用UTF-16在Windows上执行此操作的方法:

#define E_GRAVE L"\u00E8"

wchar_t * resume = L"Resum" E_GRAVE;