16位wchar_t是否正式表示完整的Unicode?
在¹comp.lang.c++ Usenet组中,我最近断言,基于我认为我所知道的,Windows的16位wchar_t
,使用UTF-16编码,有时需要两个这样的值(称为“代理对”)单个Unicode代码点,对于表示Unicode无效。
这肯定是不方便的,并且与C和C ++标准库(例如字符分类)的假设相冲突,每个代码点都表示为单个值,尽管Unicode联盟的2004年“技术说明12”为使用UTF-16提供了一个很好的例子。用于内部处理,具有令人印象深刻的软件列表。
当然,似乎最初的意图是每个代码点有一个wchar_t
值,这与C和C ++标准库的假设一致。 例如,在www.unix.org网页上的“ISO C修订1(MSE)”中,关于1995年将wchar_t
纳入C标准的修订,作者坚持认为
“一个字节/一个字符模型的主要优点是,以固定宽度的块处理数据非常容易。 出于这个原因,发明了广泛性的概念。 宽字符是一种抽象数据类型,其大小足以包含特定平台上支持的最大字符。
但事实certificate,C和C ++标准似乎没有谈论支持的最大字符,而只讨论支持的语言环境中最大的扩展字符集 : wchar_t
必须足够大才能代表最大扩展字符集中的每个代码点字符集 – 但不是Unicode,当没有Unicode语言环境时。
C99§7.17/ 2(来自N869草案):
“ [
wchar_t
type]是一个整数类型,其值范围可以表示支持的语言环境中指定的最大扩展字符集的所有成员的不同代码。
这几乎与C ++标准中的措辞完全相同。 并且它似乎意味着使用受限制的一组受支持的语言环境, wchar_t
确实可以很小,直到使用UTF-8编码的单个字节(例如,在ASCII的A到Z之外没有标准库字符分类function的噩梦可能性,但是嘿)。 可能以下要求比这更广泛:
C99§7.1.1/ 4:
“ 宽字符是
wchar_t
类型的对象的代码值(二进制编码整数),对应于扩展字符集的成员。
…因为它引用了扩展字符集,但该术语似乎无法在任何地方进一步定义。
至少在Microsoft的C和C ++运行时,没有Unicode语言环境:该实现setlocale
仅限于每个字符最多2个字节的字符编码:
MSDN的setlocale
文档:
“可用的区域设置名称,语言,国家/地区代码和代码页的集合包括Windows NLS API支持的所有内容,除了每个字符需要两个以上字节的代码页,例如UTF-7和UTF-8。 如果您提供的代码页值为UTF-7或UTF-8,则
setlocale
将失败,返回NULL
。
所以它似乎与我认为的相反,与我的断言相反,Windows的16位wchar_t
正式可以。 主要是由于Microsoft巧妙地缺乏对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一起使用,但这些API仅与实现定义的编码相关,而不是任何特定的Unicode编码。 标准库函数使用它们的实现编码知识来完成它们的工作。
那么,16位wchar_t
合法吗? 是; 该标准不要求wchar_t
足够大以容纳Unicode代码点。
是否允许wchar_t
字符串保存UTF-16值(或一般的可变宽度)? 好吧,你被允许制作wchar_t
字符串,存储你想要的任何东西(只要它适合)。 因此,就标准而言,问题是标准提供的生成wchar_t
字符和字符串的方法是否允许使用UTF-16。
那么,标准库可以做任何想做的事情; 该标准不保证从任何特定字符编码到wchar_t
的转换将是1:1映射。 甚至在标准中的任何地方都不需要通过wstring_convert
char
– > wchar_t
转换来生成1:1字符映射。
如果编译器希望声明宽字符集包含Unicode的基本多语言平面,则像这样的L'\U0001F000'
的声明将生成单个wchar_t
。 但是值是实现定义的,每[lex.ccon] / 2:
包含单个c-char的宽字符文字的值的值等于执行宽字符集中c-char的编码的数值,除非c-char在执行范围内没有表示 – 字符集,在这种情况下,值是实现定义的。
当然,C ++不允许使用代理对作为c-char ; \uD800
是编译错误。
标准中的事情变得模糊的是处理包含字符集之外的字符的字符串 。 上面的文本表明实现可以做他们想要的。 然而,[lex.string] \ 16说:
char32_t
或宽字符串文字的大小是转义序列,通用字符名称和其他字符的总数,加上一个用于终止U’\ 0’或L’\ 0’。
我说这是黑暗的,因为如果字符串文字中的c-char超出目标字符集的范围,则没有任何内容表明行为应该是什么。
Windows编译器(VS和GCC-on-Windows)确实导致L"\U0001F000"
的数组大小为3(两个代理对和一个NUL终结符)。 这是合法的C ++标准行为吗? 向字符集的有效范围之外的字符串文字提供c-char是什么意思?
我会说这是标准中的一个漏洞,而不是那些编译器的缺陷。 它应该更清楚地说明在这种情况下转换行为应该是什么。
在任何情况下, wchar_t
都不是处理Unicode编码文本的合适工具。 表示任何forms的Unicode都不是“正式有效”。 是的,许多编译器将宽字符串文字实现为Unicode编码。 但由于标准不要求这样,你不能依赖它。
很明显,你可以坚持使用适合wchar_t
。 因此,即使在wchar_t
为32位的平台上,您也可以将UTF-16数据推入其中,每个16位字占用32位。 但是您无法将此类文本传递给任何需要宽字符编码的API函数,除非您知道这是该平台的预期编码。
基本上,如果您想使用Unicode编码,请不要使用wchar_t
。
让我们从第一原则开始:
(§3.7.3) 宽字符:适合wchar_t类型的对象的位表示,能够表示当前语言环境中的任何字符
(§3.7) character:
用于组织,控制或表示数据的一组元素的成员
立即将完整的Unicode作为可在16位wchar_t
上表示的字符集(一组元素/字符)丢弃。
但等等, Nicol Bolas引用了以下内容 :
char32_t或宽字符串文字的大小是转义序列,通用字符名称和其他字符的总数,加上一个用于终止U’\ 0’或L’\ 0’。
然后想知道执行字符集之外的字符的行为。 好吧,C99对此问题有以下说法:
(§5.1.1.2)字符常量和字符串文字中的每个源字符集成员和转义序列都转换为执行字符集的相应成员; 如果没有相应的成员,则将其转换为除null(宽)字符以外的实现定义的成员.8)
并在脚注中进一步阐明并非所有源角色都需要映射到同一个执行角色。
有了这些知识,您可以声明您的广泛执行字符集是基本多语言平面,并且您将代理人视为正确的字符本身,而不仅仅是其他字符的代理人。 AFAICT,这意味着您可以清楚地了解ISO C99关注的第6条(语言)。
当然,不要指望第7章(图书馆)与你很好地相处。 例如,考虑iswalpha(wint_t)
。 你不能将星体字符(BMP之外的字符)传递给该函数,你只能将它传递给两个代理。 并且你会得到一些荒谬的结果,但这很好,因为你宣称代理人自己是执行字符集的正确成员。
澄清问题后我会做一个编辑。
问:Windows中wchar_t的16位宽度是否符合标准?
A:好吧,让我们看看。 我们将从c99 draft中定义wchar_t开始。
… 支持的语言环境中指定的最大扩展字符集。
因此,我们应该看看支持的语言环境是什么。 为此,有三个步骤:
- 我们查看setlocale的文档
-
我们快速打开语言环境字符串的文档。 我们看到字符串的格式
locale :: "locale_name" | "language[_country_region[.code_page]]" | ".code_page" | "C" | "" | NULL
-
我们看到支持的代码页面列表,我们看到UTF-8,UTF-16,UTF-32等等。 我们走到了尽头。
如果我们从C99定义开始,它以
…对应于扩展字符集的成员。
使用“字符集”一词。 但是,如果我们说UTF-16代码单元是我们的字符集,那么一切都OK。 否则,事实并非如此。 它有点模糊,人们应该不在乎。 这些标准是多年前定义的,当时Unicode不受欢迎。
在一天结束时,我们现在有C ++ 11和C11,它们定义了UTF-8,16和32的用例,其他类型为char16_t和char32_t。
您需要阅读有关Unicode的内容,您将自己回答这个问题。
Unicode是一个字符集。 字符集,大约200000个字符。 或者更准确地说,它是数字和字符之间的映射,映射。 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文本。