16位wchar_t是否正式用于表示完整的Unicode?

时间:2016-09-17 15:21:55

标签: c++ c winapi unicode encoding

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

这肯定是不方便的,并且与C和C ++标准库(例如字符分类)的假设相冲突,每个代码点都表示为单个值,尽管Unicode联盟的“2004年技术注释12”是一个很好的使用案例UTF-16用于内部处理,具有令人印象深刻的软件列表。

当然,似乎原始的 intent 每个代码点都有一个wchar_t值,这与C和C ++标准库的假设一致。例如。在ununix.org的网页“ISO C修订1(MSE)”中,关于1995年将wchar_t纳入C标准的修正案,作者坚持认为

  

单字节/单字符模型的主要优点是可以非常轻松地处理固定宽度块中的数据。出于这个原因,人们发明了广泛性的概念。宽字符是一种抽象数据类型,其大小足以包含特定平台上支持的最大字符。

但事实证明,C和C ++标准似乎没有谈论支持的最大字符,而只讨论支持的语言环境中最大的扩展字符集 :当没有Unicode语言环境时,wchar_t必须足够大才能表示最大的扩展字符集中的每个代码点 - 而不是Unicode。

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

[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文档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 功能

3 个答案:

答案 0 :(得分:4)

wchar_t现在不是,而且从来就不是Unicode字符/代码点。 C ++标准没有声明宽字符串文字将包含Unicode字符。 C ++标准没有声明宽字符文字将包含Unicode字符。实际上,该标准没有说明{em>任何关于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映射。标准中的任何位置都不需要通过char进行wchar_t - > wstring_convert转换,以生成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编码文本的合适工具。表示任何形式的 Unicode并非“正式有效”。是的,许多编译器将宽字符串文字实现为Unicode编码。但由于标准不要求这样做,所以你不能依赖它。

显然,你可以坚持wchar_t内适合的任何东西。因此,即使在wchar_t为32位的平台上,您也可以将UTF-16数据推入其中,每个16位字占用32位。但您无法将此类文本传递给任何需要宽字符编码的API函数,除非您知道这是该平台的预期编码。

基本上,如果您想使用Unicode编码,请不要使用wchar_t

答案 1 :(得分:0)

让我们从第一原则开始:

  

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

     

(§3.7) 字符: 用于该元素的一组元素的成员   组织,控制或表示数据

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

但等等,Nicol Bolas quoted the following

  

char32_t或宽字符串文字的大小是总数   转义序列,通用字符名称和其他字符,   加一个用于终止U'\ 0'或L'\ 0'。

然后想知道执行字符集之外的字符的行为。好吧,C99对此问题有以下说法:

  

(§5.1.1.2)每个源字符集成员和转义序列的字符   常量和字符串文字转换为相应的成员   执行字符集;如果没有相应的会员,   它被转换为实现定义的成员而不是   null(宽)字符.8)

并在脚注中进一步澄清,并非所有源角色都需要映射到同一个执行角色。

有了这些知识,您可以声明您的广泛执行字符集是基本多语言平面,并且您将代理人视为正确的字符本身,而不仅仅是其他字符的代理人。 AFAICT,这意味着您可以清楚地了解ISO C99关注的第6条(语言)。

当然,不要指望第7条(图书馆)与你一起玩得很好。例如,请考虑iswalpha(wint_t)。你不能将星体字符(BMP之外的字符)传递给该函数,你只能将它传递给两个代理。并且你会得到一些荒谬的结果,但这很好,因为你宣称代理人自己是执行字符集的正确成员。

答案 2 :(得分:-1)

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

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

A:好吧,让我们看看。我们将从c99 draft中定义wchar_t开始。

  

... 支持的区域设置中指定的最大扩展字符集。

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

  1. 我们会查看setlocale
  2. 的文档
  3. 我们快速打开locale string的文档。我们看到字符串的格式

    locale :: "locale_name"
            | "language[_country_region[.code_page]]"
            | ".code_page"
            | "C"
            | ""
            | NULL
    
  4. 我们看到支持的Code pages列表,我们看到UTF-8,UTF-16,UTF-32等等。我们走到了尽头。

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

    结束
      

    ...对应于扩展字符集的成员。

    使用“字符集”一词。但是,如果我们说UTF-16代码单元是我们的字符集,那么一切都可以。否则,事实并非如此。它有点模糊,人们应该不在乎。这些标准是多年前定义的,当时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文本。