如何在UTF-8中获取代码点字面量

6

我最近才意识到,C++17中的u8字符前缀并不是针对所有UTF8代码点,而只是针对ASCII部分。

来自cppreference

UTF-8字符字面量,例如u8'a'。这样的字面量具有char类型和值等于c-char的ISO 10646代码点值(假设代码点值可以用单个UTF-8代码单元表示)。如果c-char不在基本拉丁或C0控制Unicode块中,则程序无效。

auto hello = u8'嗨';     // ill-formed
auto world = u8"世";     // not a character
auto what = 0xE7958C;    // almost human-readable
auto wrong = u8"錯"[0];  // not even correct

如何简洁地获取UTF8代码点文字?编辑:对于想知道UTF8代码点如何存储的人,我发现一种合理的方法是像 Golang的方式那样。基本思想是在仅需要一个代码点时,将单个代码点存储在32位类型中。编辑2:根据有用的评论所述的论点,没有理由让编码的UTF8始终保留在32位类型中。可以将其解码,这将是utf32,并带有前缀 U ,或者将其编码为字符串,并带有前缀 u8

1
@passer 这不是 utf8。这是 utf32。u8 中的 8 代表 utf8。 - Yakk - Adam Nevraumont
1
通过上面链接中的答案中的链接,我理解它是这样的:u8(通过编译检查)保证了相应代码点的UTF-8编码适合1个字节。如果您需要其他代码点的UTF-8序列...嗯...使用U前缀进行编码,然后像往常一样将其转换为字符序列(例如通过函数)?我相信我知道你为什么问了... - Scheff's Cat
1
@passerby:“UTF-32中的每个32位值表示一个Unicode代码点,且完全等于该代码点的数值。” 代码点和字符的UTF-32表示相等。当您将Unicode编码为32位时,您正在转换为UTF-32。 - Yakk - Adam Nevraumont
3
是的。根据我浏览的内容,Golang可以让你访问码点(又称为UTF-32),而不是它的UTF-8表示方式--这有什么用呢? - Quentin
3
@user2079303说:不是这样的。它说Go源代码是utf8编码的。但是由字面量产生的实际运行时值是该utf8字面量表示的Unicode代码点。而字面量的类型是rune,它并不是可变大小的,而是int32的别名(请参阅https://golang.org/ref/spec#Numeric_types)。 - Benjamin Lindley
显示剩余13条评论
2个回答

7

如果您想要一个代码点,则应使用char32_t和前缀U

auto hello = U'嗨';

UTF-8将码点存储为8位代码单元序列。在C++中,一个char是一个代码单元,因此它无法存储完整的Unicode码点。如果提供需要多个代码单元才能存储的码点,则字符字面量上的u8前缀不会被编译,因为字符字面量仅产生一个char。
如果您想要一个以UTF8编码的单个Unicode码点,则需要使用字符串字面量,而不是字符字面量:
auto hello = u8"嗨";

我觉得一个合理的做法就像Golang一样

好吧,你不是在用Go,对吧?

在C++中,如果你要求一个字符字面量,那么你意味着该类型的单个对象。一个字面量将始终是一个。它的类型不会因字面值的内容而变化。你要求一个字符字面量,你就得到了一个字符字面量。

从你链接的网站上来看,很明显Go实际上没有UTF-8字符字面量的概念。它只有字符字面量,全部都是32位的值。实际上,在Go中所有的字符字面量都像U''那样运行。


除非我大错特错,U 不是代表 UTF32 吗? - Passer By
3
@PasserBy:我不太确定那有什么关系。你是要一个Unicode码点值还是要一个UTF-8 字符串(也就是多个码元的序列)?那么,你想要哪个呢? - Nicol Bolas
我需要将utf8和utf32之间进行转换,它们有不同的表示方式。当保持编码为utf8并将其存储在32位类型中时,我可以忽略前导空字节并附加到字符串中,而无需进行转换。 - Passer By
@PasserBy:或者你可以直接将UTF-8字符串附加到另一个字符串上。没有必要在32位整数中存储4个字符的数组。此外,Go也不会做你试图做的事情。或者至少,如果它这样做了,在你展示的页面上并没有这样做。符文文字始终是其代码点值,而不是其UTF-8编码。 - Nicol Bolas

1
在C++中,字符字面值恰好是一个字符对象。C++术语中的“字符对象”对应于Unicode中的“代码单元”。UTF-8的某些代码点需要多个代码单元。因此,并非所有UTF-8代码点都可以由单个字符对象表示。可表示的代码点是基本拉丁文和C0控制块。
要表示任何UTF-8代码点,您需要一个代码单元数组,即字符串。字符串字面值有一个类似的前缀:u8"☺"

在C++术语中,“character object”对应于Unicode编码中的“code unit”。虽然有很多不同大小的编码单元(7、8、16或32位),但是由于C++实现可以选择将“char”与Unicode编码中的一个编码单元大小对应,因此您的说法通常并不正确。 - Tom Blodget
@TomBlodget 你没有理解重点。字符对象的大小并不重要,只要它足够大以表示所有代码单元即可。关键是一个单一的字符对象不能代表一个代码单元序列。 - eerorika
我的意思是,例如,UTF-8代码单元的位数与UTF-16代码单元的位数不同。C++实现可以选择将“char”设置为8位或16位,但不能同时设置两者。 - Tom Blodget

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接