C11 Unicode支持

7
我正在编写一些类似于atoi()strtoll()的字符串转换函数。我想包含一个接受char16_t*或char32_t*而不仅仅是char*或wchar_t*的版本。
我的函数运行正常,但是在编写它时,我意识到我不明白char16_t或char32_t是什么。我知道标准只要求它们是至少16位或32位的整数类型,但暗示它们是UTF-16或UTF-32。
我还知道标准定义了一些函数,但它们没有包括任何*get或*put函数(就像在C99中添加时所做的那样)。
因此,我想知道:他们希望我如何处理char16_t和char32_t?

1
转换为UTF-8,还需要什么? - Deduplicator
@Deduplicator:如果他们希望你这样做,你会认为他们会给你相应的函数来实现... - R.. GitHub STOP HELPING ICE
你是否计划支持Unicode表中标记为“数字”的所有其他字符,而不仅仅是0..9(U+0030..U+0039)(以及潜在的A..Z/a..z,最高级别为36进制)?因为这包括排版形式(上下标;带圈数字高达20!)和特定脚本形式(阿拉伯数字,希伯来数字),以及罗马数字、古希腊和“算盘”等等。 - Jongware
@Jongware,我支持'0'-'9' 'a'-'z'/'A'-'Z'以及最高可达64进制的'A'-'Z'和'$'/'_'。 - John Vulconshinz
3个回答

9
这是一个看似无解的好问题。
C11中新增的uchar.h类型和函数基本上是没有用处的。它们只支持新类型(char16_t或char32_t)与特定区域设置、实现定义的多字节编码之间的转换,这些映射不会完整,除非该区域设置基于UTF-8。有用的转换(到/从wchar_t,以及到/从UTF-8)不受支持。当然,你可以自己实现转换到/从UTF-8,因为这些转换在相关RFC/UCS/Unicode标准中已经100%规定,但要小心:大多数人实现它们时会出现错误,并且存在危险的漏洞。
需要注意的是,在C11中新增的面向编译器的UTF-8、UTF-16和UTF-32字面量级别的新特性(u8、u和U),有潜在的用处;你可以用自己的函数对生成的字符串进行有意义的处理,而不依赖于区域设置。但是,在我看来,C11中的库级别的Unicode支持基本上是没有用的。

你知道假定多字节编码为UTF-8对可移植性有多大限制吗?(我的意思是,这是否类似于“标准允许非2的补码”,在实践中通常可以忽略,还是我真的应该担心这个问题?) - mafso
@mafso:我认为字符串的内部表示形式作为UTF8不是潜在的可移植性问题。但是,如果没有便携式(几乎肯定是“按定义”)显示结果的方法,您将如何显示文本? - Jongware
1
@Jongware:我知道这在理论上是不可移植的。但是,“特定于区域设置的多字节编码”和“特定于区域设置的宽字符编码”等内容已经在C89标准化,而当时Unicode编码并没有像今天这样广泛使用(如果我没记错,它们甚至还没有被标准化)。我的问题是,现在是否可以安全地假设Unicode编码在实践中得到了应用。 - mafso
6
在Windows操作系统中(除了Cygwin),它永远不会使用UTF-8编码。微软有一个牵强的技术借口,但真正的原因是推广他们基于UTF-16的接口而不是标准函数。 - R.. GitHub STOP HELPING ICE

3

测试UTF-16或UTF-32字符是否在ASCII范围内的“常规”10个数字,+、-或“正常”的空格之一,以及将'0'-'9'转换为数字都很容易做到。有了这个,atoi_utf16/32()就像atoi()一样进行。只需逐个检查一个字符即可。

测试其他UTF-16/UTF-32字符是否是数字或空格比较困难。代码需要扩展的isspace(),isdigit(),可以通过切换区域设置(setlocale())获取所需的区域设置。(注意:函数完成时可能需要恢复区域设置)

将通过isdigit()但不是通常的10个数字之一的字符转换为其值是有问题的。无论如何,这似乎甚至不被允许。

转换步骤:

  1. 将区域设置设置为与UTF-16/UTF-32对应的区域设置。

  2. 使用isspace()进行空格检测。

  3. 类似于your_atof()进行转换。

  4. 恢复区域设置。


不要忘记UTF-16和UTF-32都有大端和小端变体,这可能会对你很重要。 - JohnH
@JohnH:这对UTF-32有什么影响吗? - mafso
1
@mafso 大/小端变体适用于UTF-16和UTF-32。在字节级别上,2或4个字节具有与软件的字节顺序不匹配的顺序。可以使用各种字节重新排序函数来纠正这种情况。对于Unicode代码点>=0x10000,并且编码为UTF-16时,2个UTF-16代理项的顺序有时以大端或小端顺序出现。只有其中一个是正确的(忘记哪个了)。当使用不正确的代理项时,它应该标记为编码错误,尽管一些系统宽容(非投诉)。 - chux - Reinstate Monica

0

这个问题可能有点老了,但我想谈一下如何使用char16_tchar32_t实现函数。

最简单的方法是使用char32_t类型编写您的strtoull函数(称其为strtoull_c32)。这使得解析Unicode更容易,因为UTF-32中的每个字符占用四个字节。然后通过内部将UTF-8UTF-16编码转换为UTF-32并将它们传递给strtoull_c32来实现strtoull_c16strtoull_c8

老实说,我还没有看过C11标准库中的Unicode设施,但如果它们没有提供将这些类型转换为UTF-32的合适方法,那么您可以使用第三方库来进行转换。

ICU这个库,由IBM发起,然后被Unicode联盟采用。它是一个非常功能丰富且稳定的库,已经存在很长时间了。

我最近开始编写了一个C89的UTF库(UTFX),你也可以使用它。它相当简单轻便,通过了单元测试并有文档说明。你可以试试看,或者通过它来学习更多有关UTF转换的知识。


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