C++标准是否强制要求对wchar_t进行编码

Does the C++ standard mandate an encoding for wchar_t?

本文关键字:wchar 编码 是否 标准 C++      更新时间:2023-10-16

以下是我的2014年标准草案N4140 的一些摘录

22.5标准代码转换方面[locale.stdcvt]

3对于三个代码转换方面codecvt_utf8codecvt_utf16codecvt_utf8_utf16中的每一个:
(3.1)--Elem是宽字符类型,例如wchar_tchar16_tchar32_t

4对于面codecvt_utf8:
(4.1)-方面应在UTF-8多字节序列和程序中的UCS2或UCS4(取决于Elem的大小)之间转换。

对这两段的一种解释是wchar_t必须编码为UCS2或UCS4。我不太喜欢它,因为如果它是真的,我们在图书馆的描述中有一个语言的重要特性。我试图找到一个更直接的关于这一财产的陈述,但没有成功。

另一种解释是,wchar_t编码不需要是UCS2或UCS4,在不需要的实现中,codecvt_utf8不适用于wchar_t。我也不太喜欢这种解释,因为如果这是真的,并且charwchar_t的原生编码都不是Unicode,那么似乎没有办法在这些原生编码和Unicode之间进行可移植的转换。

这两种解释中哪一种是正确的?还有一个我忽略了吗?

澄清我不是在询问关于wchar_t适用于软件开发的一般意见,或者wchar_t的特性可以从其他地方获得。我对标准的这两个特定段落感兴趣。我正在努力理解这些特定段落包含什么或不包含什么。

澄清2。如果4.1中说"facet应在UTF-8多字节序列和UCS2或UCS4之间转换,或者当前全局语言环境对wchar_t施加的任何编码",则没有问题。事实并非如此。它说什么就说什么。看起来,如果使用std::codecvt_utf8<wchar_t>,那么不管当前的全局语言环境如何,最终都会得到一堆编码为UCS2或UCS4的wchar_t。(无法为codecvt_utf8指定区域设置或任何字符转换方面)。因此,这个问题可以这样表述:转换结果是否可以直接用于当前全局语言环境(和/或任何可能的语言环境)以用于输出、wctype查询等?如果不是,可用什么?(如果上面的第二种解释是正确的,答案似乎是"什么都没有")。

wchar_t只是一个整数。它有一个最小值、一个最大值等

它的尺寸不是标准规定的。

如果它足够大,可以将UCS-2或UCS-4数据存储在wchar_t的缓冲区中。无论您所在的系统是什么,这都是正确的,因为UCS-2和UCS-4以及UTF-16和UTF-32只是对按序列排列的整数值的描述。

在C++11中,有stdAPI可以读取或写入数据,前提是数据具有这些编码。在C++03中,有一些API使用当前区域设置读取或写入数据。

22.5标准代码转换方面[locale.stdcvt]

3对于三个代码转换方面中的每一个,codecvt_utf8、codecvt_outf16和codecvt_utf8_utf16:

(3.1)-Elm是宽字符类型,例如wchar_t、char16_t或char32_t。

4对于面代码cvt_utf8:

(4.1)-方面应在程序中的UTF-8多字节序列和UCS2或UCS4(取决于Elem的大小)之间转换。

因此,在这里,codecvt_utf8_utf16在一侧处理utf8,在另一侧处理UCS2或UCS4(取决于Elem的大小)。它进行转换。

Elem(宽字符)被认为是在UCS2或UCS4中编码的,这取决于它的大小

这并不意味着wchar_t被如此编码,它只是意味着此操作将wchar_t解释为被如此编码

UCS2或UCS4是如何进入Elem的,这部分标准并不关心。也许你用十六进制常量设置它。也许你是从io那里读到的,也许你是在飞行中计算出来的。也许你使用了高质量的随机数生成器。也许您将ascii字符串的位值加在一起。也许你计算了log*的定点近似值,即月球将地球的一天改变1秒所需的秒数不是这些段落的问题这些练习图只是规定如何修改和解释位。

类似的主张也适用于其他情况。这并不强制要求wchar_t具有什么格式。它简单地说明了这些方面如何解释wchar_tchar16_tchar32_tchar8_t(读或写)。

wchar_t交互的其他方式使用不同的方法来规定如何解释wchar_t的值。

例如,iswalpha使用(全局)语言环境来解释wchar_t。在一些局部中,wchar_t可以是UCS2。在其他情况下,它可能是一些疯狂的cthulian编码,其细节使你能够从太空外看到一种新的颜色。

明确地说:编码不是数据或位的属性。编码是数据解释的属性。通常只有一个正确合理的数据解释是有意义的,但数据本身就是比特。

C++标准并不强制要求存储在wchar_t中的内容。它确实规定了某些操作将wchar_t的内容解释为什么。该部分描述了某些方面如何解释wchar_t中的数据。

编号。

wchar只需要保存编译器支持的最大区域设置。理论上可以放在炭里。

类型wchar_t是一种不同的类型,其值可以表示在支持的区域设置(22.3.1)中指定的最大扩展字符集的所有成员的不同代码

--C++〔basic.basic〕3.9.1/5

因此,它甚至不需要支持Unicode

wchar_t的宽度是编译器特定的,可以小到8位。因此,需要在任何C或C++编译器中移植的程序都不应该使用wchar_t来存储Unicode文本。wchar_t类型用于存储编译器定义的宽字符,这些字符在某些编译器中可能是Unicode字符。

ISO/IEC 10646:2003 Unicode标准4.0

让我们区分wchar_t和使用L前缀构建的字符串文字。

wchar_t只是一个整数类型,它可能大于char

使用L前缀的字符串文字将生成使用wchar_t字符的字符串。这到底意味着什么取决于实现。不要求此类文字使用任何特定编码。他们可能使用UTF-16、UTF-32或其他与Unicode完全无关的东西。

因此,如果希望在所有平台上以Unicode格式编码保证的字符串文字,请为字符串文字使用u8uU前缀。

对这两段的一种解释是wchar_t必须被编码为UCS2或UCS4。

不,这不是一个有效的解释。wchar_t没有编码;这只是一种类型。编码的是数据。以L为前缀的字符串文字可以在UCS2或UCS4中编码,也可以不编码。

如果为codecvt_utf8提供一个在UCS2或UCS4中编码的wchar_t字符串(适用于sizeof(wchar_t)),则它将起作用。但不是因为wchar_t;它之所以有效,是因为您提供的数据编码正确。

如果4.1中说"facet应在UTF-8多字节序列和UCS2或UCS4之间转换,或者当前全局语言环境对wchar_t施加的任何编码",则没有问题。

这些codecvt_*方面的全部目的是执行与语言环境无关的转换。如果您想要依赖于区域设置的转换,则不应该使用它们。您应该使用全局codecvt方面。

微软似乎分享了你的第一个结论,他们列举了可能的选项,并注意到UTF-16,尽管"广泛使用"不是一种有效的编码。

QNX也使用了相同的措辞,指出了措辞的来源:QNX和微软的标准库实现都来自Dinkumware。

现在,碰巧的是,Dinkumware也是N2401的作者,该书介绍了这些类。所以我要站在他们一边。

由于Elem可以是wchar_tchar16_tchar32_t,因此条款4.1没有说明所需的wchar_t编码。它说明了所执行的转换。

从措辞中可以清楚地看出,转换是在UTF-8和UCS-2或UCS-4之间进行的,这取决于Elem的大小。因此,如果wchar_t是16位,则转换将使用UCS-2,如果是32位,则使用UCS-4。

为什么标准中提到了UCS-2和UCS-4,而没有提到UTF-16和UTF-32?因为codecvt_utf8将多字节UTF8转换为单个宽字符:

  • UCS-2是unicode的一个子集,但没有与UTF-16相反的附加对编码
  • 现在,UCS-4与UTF-32相同(但随着表情符号的数量不断增加,也许有一天32位的表情符号不够,你会有一个codecvt_utf8不支持的UTF-64和UTF32代理对)

但是,我不清楚如果UTF-8文本包含与接收char16_t的UCS-2中不可用的unicode字符相对应的序列,会发生什么

您的两种解释都不正确。该标准不要求有单个wchar_t编码,就像它不需要单个char编码一样。codecvt_utf8方面必须在UTF-8和UCS-2或UCS-4之间转换。即使UTF-8、UCS-2和UCS-4在任何语言环境中都不支持作为字符集。

如果Elem属于wchar_t类型,并且不足以存储UCS-2值,那么codecvt_utf8方面的转换操作是未定义的,因为标准没有说明在这种情况下会发生什么。如果它足够大(或者如果你想争论标准要求它必须足够大),那么它只是实现定义的,即方面生成或使用的UCS-2或UCS-4 wchar_t值的编码是否与任何语言环境定义的wchar_t编码兼容。

第一个解释条件成立。

如果定义了__STDC_ISO_10646__宏(从C导入),则wchar_t是某个Unicode版本的超集。

__STDC_ISO_10646__
形式为yyyymmL(例如199712L)的整数文字。如果定义了此符号,则每个存储在类型为wchar_t的对象中时,Unicode必需集中的字符具有相同的值作为该字符的短标识符。Unicode必需集由以下所有字符组成由ISO/IEC 10646定义,以及规定的所有修订和技术勘误年和月。

看来,如果定义了宏,就可以假设某种UCS4。(不是UCS2,因为ISO 10646从来没有16位版本;ISO 10646的第一个版本对应于Unicode 2.0)

因此,如果定义了宏,那么

  • 存在"本机"wchart编码
  • 它是UCS4某个版本的超集
  • codecvt_utf8<wchar_t>提供的转换与这种本地编码兼容

如果未定义宏,则不需要保留这些内容。

还有__STDC_UTF_16____STDC_UTF_32__,但C++标准没有说明它们的含义。C标准规定它们分别表示char16_tchar32_t的UTF-16和UTF-32编码,但在C++中总是使用这些编码。

顺便提及,函数mbrtoc32c32rtombchar序列和char32_t序列之间来回转换。在C中,如果定义了__STDC_UTF_32__,它们只使用UTF-32,但在C++中,UTF-32总是用于char32_t。因此,即使__STDC_ISO_10646__而不是定义的,也应该可以通过从UTF-8到UTF-32编码的char32_t到本机编码的char再到本机编码器的wchar_t在UTF-8和wchar_t之间转换,但我担心这种复杂的东西。