C ++ 11中的Unicode标识符和源代码?

时间:2011-04-15 12:49:17

标签: unicode syntax c++11

我在新的C ++标准中找到了

2.11 Identifiers                  [lex.name]
identifier:
    identifier-nondigit
    identifier identifier-nondigit
    identifier digit
identifier-nondigit:
    nondigit
    universal-character-name
    other implementation-defined character

附加文字

  

标识符是任意长的字母和数字序列。标识符中的每个通用字符名称应指定一个字符,其ISO 10646中的编码属于指定范围之一   在E.1中。 [...]

我无法理解这意味着什么。从旧的标准我已经习惯了,例如,“通用字符名称”被写成\u89ab。但是在标识符中使用那些......?真的?

新标准是否更加开放,是否符合Unicode?而且我没有引用新的文字类型 "uHello \u89ab thing"u32,我想我理解了这些。但是:

  • 可以(便携式)源代码采用任何unicode编码,如UTF-8,UTF-16或任何(如何定义的)代码页?
  • 我可以在其中\u1234编写一个标识符myfu\u1234ntion(用于任何目的)
  • 或者我可以使用unicode在ICU中定义的“字符名称”,即

    const auto x = "German Braunb\U{LOWERCASE LETTER A WITH DIARESIS}r."u32;
    

    甚至在源本身的标识符中?这将是一种享受...... 咳嗽 ......

我认为所有这些问题的答案都是 no ,但我无法将其可靠地映射到标准中的措辞......: - )

编辑:我找到了“2.2翻译阶段[lex.phases]”,第1阶段:

  

如果需要,物理源文件字符以实现定义的方式映射到基本源字符集[...]。接受的物理源文件字符集是实现定义的。 [...]任何源文件字符都不在基本字符中   源字符集(2.3)由指定该字符的通用字符名称替换。 (实现可以使用任何内部编码,只要在源文件中遇到实际扩展字符,并且在源文件中表示为与通用字符名称相同的扩展字符(即,使用\ uXXXX表示法),处理等效,除非在原始字符串文字中还原此替换。)

通过阅读本文,我现在认为,编译器可以选择接受UTF-8,UTF-16或其希望的任何代码页(通过元信息或用户配置)。在阶段1中,它将其转换为ASCII格式(“基本源字符集”),然后将Unicode字符替换为其\uNNNN表示法(或者编译器可以选择继续以其Unicode表示形式工作) ,但必须确保它以相同的方式处理另一个\uNNNN

您怎么看?

5 个答案:

答案 0 :(得分:11)

  

新标准是否更符合Unicode?

关于在标识符中允许通用字符名称,答案是否定的;在C99和C ++ 98中允许使用UCN标识符。然而,编译器直到最近才实现该特定要求。 Clang 3.3我认为引入了对此的支持,GCC已经为此做了一段时间的实验性功能。 Herb Sutter在他的Build 2013演讲“C ++的未来”中也提到过,这个特性在某些时候也会出现在VC ++中。 (尽管IIRC Herb将其称为C ++ 11特性;它实际上是C ++ 98特性。)

预计标识符不会使用UCN编写。相反,预期的行为是使用源编码编写所需的字符。例如,来源将如下:

long pörk;

long p\u00F6rk;

然而,UCN也可用于其他目的;编译器并不都需要接受相同的源编码,但现代编译器都支持一些编码方案,其中至少基本源字符具有相同的编码(即,现代编译器都支持某些ASCII兼容编码)。

UCN允许您只使用基本字符编写源代码,但仍然可以命名扩展字符。这在例如在源代码中编写字符串文字“°”时非常有用,它将被编译为CP1252和UTF-8:

char const *degree_sign = "\u00b0";

这个字符串文字在多个编译器上被编码为适当的执行编码,即使源编码不同,只要编译器至少对基本字符共享相同的编码。

  

可以(便携式)源代码采用任何unicode编码,如UTF-8,UTF-16或任何(如何定义的)代码页?

标准并不要求,但大多数编译器都会接受UTF-8源代码。 Clang仅支持 UTF-8源(虽然它对字符和字符串文字中的非UTF-8数据有一定的兼容性),gcc允许指定源编码并包含对UTF-8的支持,并且VC ++会猜测编码并且可以猜测UTF-8。

(更新:VS2015现在提供option来强制源和执行字符集为UTF-8。)

  

我可以用\ u1234在其中写一个标识符myfu \ u1234ntion(出于任何目的)

是的,规范要求这样做,尽管我说并非所有编译器都实现了这个要求。

  

或者我可以使用unicode在ICU中定义的“字符名称”,即

const auto x = "German Braunb\U{LOWERCASE LETTER A WITH DIARESIS}r."u32;

不,您不能使用Unicode长名称。

  

甚至在源本身的标识符中?那将是一种享受......咳嗽......

如果编译器支持包含所需扩展字符的源代码编码,那么字面上在源中写入的字符必须与等效的UCN完全相同。所以,是的,如果您使用支持C ++规范的这个要求的编译器,那么您可以直接在源代码中编写源字符集中的任何字符,而无需编写UCN。

答案 1 :(得分:2)

我认为目的是在标识符中允许使用Unicode字符,例如:

long pöjk;
ostream* å;

答案 2 :(得分:1)

这篇文章https://www.securecoding.cert.org/confluence/display/seccode/PRE30-C.+Do+not+create+a+universal+character+name+through+concatenation适用于int \u0401;是兼容代码的想法,尽管它基于C99,而不是C ++ 0x。

答案 3 :(得分:1)

目前的gcc版本(目前为止版本为5.2)仅支持ASCII,在某些情况下仅支持EBCDIC输入文件。因此,必须使用ASCII编码文件中的\ uXXXX和\ UXXXXXXXX转义序列来表示标识符中的unicode字符。虽然可以在EBCDIC编码的输入文件中将unicode字符表示为?? / uXXXX和?? / UXXXXXXX,但我还没有对此进行测试。在任何情况下,只要安装了最新版本的iconv,就可以通过简单的cpp单行补丁直接读取UTF-8输入。详情见

https://www.raspberrypi.org/forums/viewtopic.php?p=802657

可以通过补丁进行总结

diff -cNr gcc-5.2.0/libcpp/charset.c gcc-5.2.0-ejo/libcpp/charset.c
*** gcc-5.2.0/libcpp/charset.c  Mon Jan  5 04:33:28 2015
--- gcc-5.2.0-ejo/libcpp/charset.c  Wed Aug 12 14:34:23 2015
***************
*** 1711,1717 ****
    struct _cpp_strbuf to;
    unsigned char *buffer;

!   input_cset = init_iconv_desc (pfile, SOURCE_CHARSET, input_charset);
    if (input_cset.func == convert_no_conversion)
      {
        to.text = input;
--- 1711,1717 ----
    struct _cpp_strbuf to;
    unsigned char *buffer;

!   input_cset = init_iconv_desc (pfile, "C99", input_charset);
    if (input_cset.func == convert_no_conversion)
      {
        to.text = input;

答案 4 :(得分:1)

我建议使用clang++代替g++。 Clang旨在与GCC(wikipedia-source)高度兼容,因此您很可能只是替换该命令。

我想在源代码中使用希腊符号。 如果代码可读性是目标,那么使用(例如)α而不是alpha似乎是合理的。特别是在较大的数学公式中使用时,可以在源代码中更容易地读取它们。

为实现这一目标,这是一个最小的工作示例:

> cat /tmp/test.cpp
#include <iostream>

int main()
{
    int α = 10;
    std::cout << "α = " << α << std::endl;
    return 0;
}
> clang++ /tmp/test.cpp -o /tmp/test
> /tmp/test 
α = 10