C++23:char 现在支持 Unicode?

C++23: char now supports Unicode?

提问人:mishar 提问时间:9/7/2023 更新时间:10/5/2023 访问量:1590

问:

C++23 现在是否在其基本类型中提供对 Unicode 字符的支持,以及在多大程度上?char


因此,在字符文本的 cppreference 上,字符文本:

'c-char'

定义为:

  • 一个basic-c-char
  • 转义序列,如转义序列中定义
  • 通用字符名称,如转义序列中定义

然后对于 ,它被定义为:basic-c-char

基本源字符集(直到 C++23)翻译字符集(自 C++ 23 起)中的字符,单引号、反斜杠或换行符除外'\

然后,在字符集的 cpppreference 页面上,它将“翻译字符集”定义为由以下内容组成:

  • 每个抽象字符在 Unicode 代码空间中分配一个码位,并且(从 C++ 23 开始)
  • 每个 Unicode 标量值的不同字符,未分配给抽象字符。

并指出:

翻译字符集是基本字符集和基本文字字符集的超集(见下文)。

在我看来,“基本字符集”(在上面的页面上给出)基本上是 ASCII 的子集。我也一直认为是ASCII(支持ISO-8859字符集,例如根据Microsoft的字符类型页面)。但是现在随着对 的翻译字符集的更改,它似乎在某种程度上更全面地支持 Unicode。charbasic-c-char

我知道实际的编码是定义的实现(除了空字符和递增的十进制数字字符)。但我的主要问题是,这个“翻译字符集”真正支持哪些字符?是Unicode的全部吗?我觉得我读的比实际情况要多。

C++ Unicode 字符编码 char 文本

评论

2赞 dan04 9/7/2023
标准中一堆奇怪的措辞基本上是在说“我们希望C++实现支持Unicode,但我们不想仅仅因为它或其平台是非Unicode感知的,就将任何现有代码声明为非标准代码。
2赞 Giacomo Catenazzi 9/7/2023
“支持Unicode”是什么意思?如果你认为更准确,也许你可以回答。简而言之:只需将数据保存为黑盒字符串(例如UTF-8)。在输入和输出上,对黑匣子预定义格式进行转换(没有人可以猜测输入和输出的预期编码,所以也不是C++标准)。对于处理,您需要良好的 Unicode 库(不要认为单个代码点是处理 unicode 字符串的好单位)。
0赞 dan04 9/7/2023
utf8everywhere.org

答:

5赞 eerorika 9/7/2023 #1

这个“翻译字符集”真正支持哪些字符?

正如您已经引用的(我将引用最新的 C++ 标准草案):

[lex.字符集]

翻译字符集由以下元素组成:

  • 在 Unicode 代码空间中分配一个代码点的每个抽象字符,以及
  • 每个 Unicode 标量值的不同字符,未分配给抽象字符。

让我们查找规则中使用的术语的定义(引自 Unicode 14):

对于第一点:

字符和编码

抽象字符:用于组织的信息单位, 文本数据的控制或表示。

  • 在表示数据时,该数据的性质通常是符号性的,而不是某种其他类型的数据(例如,听觉或 视觉)。此类符号数据的示例包括字母、表意文字、 数字、标点符号、技术符号和 dingbats。
  • 抽象字符没有具体形式,不应与字形混淆。
  • 抽象字符不一定对应于用户认为的“字符”,不应与字素混淆。
  • 由 Unicode 标准编码的抽象字符称为 Unicode 抽象字符。
  • Unicode 标准未直接编码的抽象字符通常可以通过使用组合字符序列来表示

对于第二点:

Unicode 编码形式

Unicode 标量值:除高代理项和低代理项代码点之外的任何 Unicode 码位。

  • 由于此定义,Unicode 标量值集由 范围 0 到 D7FF 16 和 E000 16 到 10FFFF 16(含)。

C++ 标准还有一个澄清说明:

[注1:Unicode码位是[0, 10FFFF]范围内的整数 (十六进制)。代理码位是 [D800, DFFF](十六进制)。Unicode 标量值是任何符合以下条件的代码点 不是代理代码点。——尾注]


是Unicode的全部吗?

TLDR:没有。例如。代理码位和组合字符序列不在翻译字符集中。

此外,这是来自 C++ 的重要规则:

具有 c-char-sequence 的字符文本由单个 basic-c-char、simple-escape-sequence 或 universal-character-name 组成,是指定字符的代码单元值,在文本的关联字符编码中编码。如果指定的字符在文本的关联字符编码中缺少表示形式,或者无法将其编码为单个代码单元,则程序的格式不正确。

如果您的系统具有 8 位,则它将无法表示 Unicode 代码空间的所有 10FFFF 码位。char


附言文字中的Unicode从未被C++标准禁止;此更改只是强制要求对 Unicode 支持。char

评论

0赞 Yakk - Adam Nevraumont 9/7/2023
lex.charset 不是可以编写 C++ 代码的字符集,而不是 C++ 库处理的字符集吗?
0赞 eerorika 9/7/2023
@Yakk-AdamNevraumont 是的。据我所知,这个问题是关于字符字面的。库可以处理它们想要的任何字符集。
3赞 n. m. could be an AI 9/7/2023
“如果你的系统有一个 8 位字符,那么它将无法表示 Unicode 代码空间的所有 10FFFF 代码点”一个 8 位文字今天显然无法做到这一点,而 C++23 也不会神奇地赋予它这种能力。然而,字符串文字可能能够代表所有 Unicode,而 C++23 不会剥夺这种能力(这个问题没有提到字符串文字,但恕我直言应该是一个很好的答案)。char
16赞 user17732522 9/7/2023 #2

实际上变化不大(有两个重要区别):

在 C++23 之前,第一个转换阶段定义源文件中任何不是基本源字符集(它是 ASCII 字符集的子集)元素的字符都将映射到通用字符名称,即它将被替换为格式的序列,其中是字符的 ISO/IEC 10646(等效于 Unicode)代码点的编号。\UXXXXXXXXXXXXXXXX

然后,在编写字符文本时,其中替换为不在基本源字符集中的字符,您将在第一个翻译阶段之后获得,然后应用 c-char -> 通用字符名称语法'X'X'\UXXXXXXXX'

因此,您始终可以在字符文本中写入非 ASCII 字符,前提是源编码允许写入此类字符。源文件编码和基本源字符集之外支持的源字符被实现定义为源字符集(编码)。无论源字符集如何,您都可以将任何 Unicode 标量值直接写入具有通用字符名称的字符文本中。

然后,此字符文本将如何表现是一个不同的问题,因为用于确定通用字符名称(或基本源字符集的任何字符)的值的编码也是实现定义的(C++20 中的执行字符集编码或 C++23 中的普通文本编码)。显然,如果宽度为 8 位,则不能表示所有 Unicode 标量值。如果字符在 中不可表示,则行为是实现定义的。charcharchar

C++23 的更改现在是对 UTF-8 源编码的支持成为强制性的,这意味着支持源文件中的所有 Unicode 标量值(当然也可以支持其他编码),并且第一阶段已更改,因此不是通过通用字符名称将所有内容重写为基本源字符集, 现在,源字符被映射到一个转换字符集序列,该序列本质上是一个Unicode标量值序列。不是 Unicode 标量值(即代理码位)的 Unicode 码位不是转换字符集的元素(并且不能通过解码任何源文件来生成)。

因此,在 C++23 中,当进入确定字符文本值的转换阶段时,源文件中的单个 Unicode 标量值与您在问题中显示的 basic-c-char 语法匹配。

字符文本的值仍像以前一样由实现定义的编码确定。但是,与 C++20 相比,如果字符无法通过此编码表示,则文本现在格式不正确。char

因此,两个区别是必须支持 UTF-8 源文件编码,并且字符文本中的单个源字符(即单个 Unicode 标量值)在实现定义的普通文本编码中无法表示,现在将导致文本格式错误,而不是具有实现定义的值。


与上述类似,字符串文本(而不是字符文本)也没有真正改变。编码仍然是使用相同的普通文字编码实现定义的,并且主要只是更改了翻译阶段中的内部表示形式。与字符文本一样,使用 C++23 时,如果字符(即翻译字符集元素或 Unicode 标量值)在普通文本字符编码中无法表示,则文本会变得格式错误。但是,该编码可能是 UTF-8,因此源文件中的单个 Unicode 标量值可以映射到编码字符串中的多个标量值,就像一直以来的情况一样。char