16位wchar_t对于表示完整的Unicode是否正式有效

Is 16-bit wchar_t formally valid for representing full Unicode?

本文关键字:Unicode 是否 有效 表示 wchar 于表示 16位      更新时间:2023-10-16

在1 comp.lang.c++ Usenet组中,我最近断言,基于我认为我知道的,Windows的16位wchar_t,使用UTF-16编码,有时需要两个这样的值(称为“代理对”)来表示单个Unicode码位,对于表示Unicode是无效的。

这当然是不方便的,并且与C和c++标准库的假设(例如字符分类)相冲突,每个代码点都表示为单个值,尽管Unicode联盟2004年的²Technical Note 12很好地说明了使用UTF-16进行内部处理,并列出了令人印象深刻的软件列表。

当然,似乎最初的意图是每个代码点有一个wchar_t值,与C和c++标准库的假设一致。例如,在网页"ISO C修正案1 (MSE)"中;在³unix.org上,关于1995年将wchar_t引入C标准的修订,作者坚持认为

一个字节/一个字符模型的主要优点是非常容易处理固定宽度块中的数据。因此,宽字符的概念被发明出来。宽字符是一种抽象数据类型,其大小足以容纳特定平台上支持的最大字符。

但事实证明,C和c++标准似乎没有讨论支持的最大字符,而只讨论支持的地区中最大的扩展字符集: wchar_t必须足够大,以表示最大的扩展字符集& naddash中的每个代码点;如果没有Unicode区域设置,则不使用Unicode。

C99§7.17/2(来自N869草案):

[the wchar_t type]是一个整数类型,其值范围可以表示支持的语言环境中指定的最大扩展字符集的所有成员的不同代码。

这与c++标准中的措辞几乎完全相同。这似乎意味着,在一组受限制的支持区域设置下,wchar_t确实可以更小,直到使用UTF-8编码的单个字节(一个噩梦般的可能性,例如,没有标准库字符分类函数将在ASCII的a到Z之外工作,但是嘿)。

<我> C99§7.1.1/4:

宽字符wchar_t类型对象的一个码值(一个二进制编码的整数),它对应于扩展字符集的一个成员。

白马王子;因为它指的是扩展字符集,但这个术语似乎没有在任何地方进一步定义。

并且至少在Microsoft的C和c++运行时中没有Unicode区域设置:在该实现中,setlocale被限制为每个字符最多2字节的字符编码:

MSDN⁴documentation of setlocale:

可用的区域名称、语言、国家/地区代码和代码页集包括Windows NLS API支持的所有代码页,除了每个字符需要超过两个字节的代码页,例如UTF-7和UTF-8。如果您提供的代码页值为UTF-7或UTF-8,则setlocale将失败,返回NULL

因此,似乎与我所知道的相反,与我的断言相反,Windows的16位wchar_t在形式上是OK的。这主要是由于微软缺乏对UTF-8语言环境的支持,或者任何每个字符超过2字节的语言环境。但真的是这样吗,16位wchar_t可以吗?


<一口>链接:
¹新闻:comp.lang.c + +
²http://unicode.org/notes/tn12/Software_16
³http://www.unix.org/version2/whatsnew/login_mse.html
⁴https://msdn.microsoft.com/en-us/library/x99tb11d.aspx

wchar_t现在不是,从来都不是Unicode字符/码点。c++标准没有声明宽字符串字面值将包含Unicode字符。c++标准没有声明宽字符文字必须包含Unicode字符。事实上,标准并没有说任何关于wchar_t将包含什么。

wchar_t可以与语言环境感知的api一起使用,但这些仅相对于实现定义的编码,而不是任何特定的Unicode编码。使用这些函数的标准库函数使用它们对实现编码的了解来完成它们的工作。

那么,16位wchar_t合法吗?是的,标准不要求wchar_t足够大以容纳Unicode码点。

wchar_t的字符串允许持有UTF-16值(或一般的可变宽度)?你可以使用wchar_t的字符串来存储你想要的任何东西(只要合适)。因此,就标准而言,问题是标准提供的用于生成wchar_t字符和字符串的方法是否允许使用UTF-16。

标准库可以做任何它想做的事;该标准不保证从任何特定字符编码到wchar_t的转换将是1:1的映射。即使char -> wchar_t转换通过wstring_convert也不需要在标准的任何地方产生1:1的字符映射。

如果编译器希望声明宽字符集由Unicode的基本多语言平面组成,那么像L'U0001F000'这样的声明将产生单个wchar_t。但是值是由实现定义的,per [lex.ccon]/2:

包含单个c字符的宽字符字面量的值等于执行宽字符集中该c字符编码的数值,除非该c字符在执行宽字符集中没有表示,在这种情况下,该值是实现定义的。

当然,c++不允许使用代理对作为 C -char;uD800是一个编译错误。

在标准中变得模糊的地方是字符串包含字符集之外的字符的处理。上述文本表明,实现可以做他们想做的事情。然而,[莱克斯]。字符串]16表示:

char32_t或宽字符串字面值的大小是转义序列、通用字符名和其他字符的总数加上一个用于终止U ' '或L ' '。

我说这是模糊的,因为没有说明如果字符串字面值中的c-char在目标字符集的范围之外应该是什么行为。

Windows编译器(VS和Windows上的gcc)确实会导致L"U0001F000"的数组大小为3(两个代理对和一个NUL终止符)。这是合法的c++标准行为吗?为字符集的有效范围之外的字符串字面值提供c-char是什么意思?

我想说这是标准中的一个漏洞,而不是那些编译器的缺陷。在这种情况下,它应该使转换行为更清楚。


无论如何,wchar_t都不是处理unicode编码文本的合适工具。对于表示任何形式的Unicode 都不是"正式有效的"。是的,许多编译器将宽字符串字面值实现为Unicode编码。但是由于标准没有要求这样做,所以您不能依赖它。

现在很明显,你可以把任何适合wchar_t的东西放在里面。因此,即使在wchar_t是32位的平台上,您也可以将UTF-16数据塞进去,每个16位字占用32位。但是你不能将这样的文本传递给任何期望宽字符编码的API函数,除非你知道这是该平台的期望编码。

基本上,如果你想使用Unicode编码,就不要使用wchar_t

让我们从第一原则开始:

(§3.7.3) 宽字符:位表示,适合类型的对象Wchar_t,能够表示当前语言环境中的任何字符

(§3.7) 字符: 用于数据的组织、控制或表示

,马上,丢弃完整的Unicode作为一个字符集(一组元素/字符),可在16位wchar_t上表示。

但是等等,nicolbolas引用了以下内容:

char32_t或宽字符串字面值的大小是字符的总数转义序列、通用字符名和其他字符;加上一个U ' '或L ' '。

,然后想知道执行字符集之外的字符的行为。嗯,C99对这个问题有以下看法:

(§5.1.1.2)每个源字符集成员和字符中的转义序列常量和字符串字面值转换为相应的成员执行字符集的;如果没有对应的成员,它被转换为实现定义的成员,而不是Null (wide) character.8)

,并在脚注中进一步澄清,并非所有源字符都需要映射到相同的执行字符。

有了这些知识,你就可以声明你的广泛执行字符集是基本多语言平面,并且你把代理看作是合适的字符本身,而不仅仅是其他字符的代理。AFAICT,这意味着只要ISO C99条款6(语言)关心,您就清楚了。

当然,不要指望第7条(库)会很好地配合您。以iswalpha(wint_t)为例。您不能将星光字符(BMP之外的字符)传递给该函数,您只能将两个代理传递给它。您可能会得到一些无意义的结果,但这没关系,因为您将代理本身声明为执行字符集的适当成员。

澄清问题后,我将进行编辑。

问:Windows中wchar_t的16位宽度是否符合标准?

A:嗯,让我想想。我们将从c99草案中的wchar_t定义开始。

支持的语言环境中指定的最大扩展字符集。

所以,我们应该看看支持的语言环境是什么。有三个步骤:

    我们检查setlocale的文档
  1. 我们快速打开文档查找区域设置字符串。我们可以看到字符串

    的格式
    locale :: "locale_name"
            | "language[_country_region[.code_page]]"
            | ".code_page"
            | "C"
            | ""
            | NULL
    
  2. 我们看到支持的代码页列表,我们看到UTF-8, UTF-16, UTF-32等等。

如果我们从C99定义开始,它以

结束

…对应于扩展字符集的一个成员。

使用"字符集"这个词。但是如果我们说UTF-16代码单元是我们的字符集,那么一切都没问题。否则,它就不是。这有点模糊,人们不应该太在意。这些标准是多年前定义的,当时Unicode还不流行。

在一天结束时,我们现在有c++ 11和C11定义了UTF-8, 16和32的用例,并添加了char16_t和char32_t类型。


你需要阅读Unicode,你将自己回答这个问题。

Unicode是字符集。字符集,大约有20万个字符。或者更准确地说,它是一种映射,数字和字符之间的映射。Unicode本身并不表示这个或那个位宽。

然后有4种编码,UTF-7, UTF-8, UTF-16和UTF-32。UTF代表Unicode转换格式。每种格式定义一个代码点和一个代码单元。代码点是来自Unicode的实际章程,可以由一个或多个单元组成。只有UTF-32每个点有一个单位。

另一方面,每个单位都适合一个固定大小的整数。因此,UTF-7单位最多为7位,UTF-16单位最多为16位,等等。

因此,在16位wchar_t字符串中,我们可以保存以UTF-16编码的Unicode文本。特别是在UTF-16中,每个点占用一个或两个单位。

所以最后的答案,在单个wchar_t中你不能存储所有Unicode字符,只能存储单个单位字符,但在wchar_t字符串中你可以存储任何Unicode文本。