UTF-16和UTF-8兼容吗?

11
我向Google提出了上述问题,并被导向UTF-8和UTF-16之间的区别?,但很遗憾它并没有回答我的问题。
据我理解,UTF-8应该是UTF-16的子集,这意味着:如果我的代码使用UTF-16,而我传递的是UTF-8编码的字符串,则一切都应该没问题。反过来(期望UTF-8,收到UTF-16)可能会引起问题。
这是否正确?
编辑:为了阐明为什么链接的SO问题不能回答我的问题:当尝试使用WebClient.DownloadString处理JSON字符串时,我的问题出现了,因为WebClient使用了错误的编码。我从请求中接收到的JSON以UTF-8编码,我的问题是:如果我设置webClient.Encoding = New System.Text.UnicodeEncoding(即UTF-16),我能否处于安全状态,即能够处理UTF-8和UTF-16请求结果,还是应该使用webClient.Encoding = New System.Text.UTF8Encoding

7
不,它们不兼容。 - i486
2
“Hand in”是什么意思?它们编码相同的字符集,但如果将UTF-8字节序列解释为UTF-16,则不会表示相同的字符集。如果您能提供更多关于您尝试做什么的细节,那将非常有帮助。 - Jon Skeet
2
可能是以下问题的重复:UTF-8和UTF-16之间有何区别? - tripleee
3
每位程序员都必须了解编码和字符集以处理文本。 - deceze
不,那是不正确的。并非所有UTF-8编码的字节都是有效的UTF-16字节,反之亦然。没有办法选择正确的编码来处理两者;您需要知道输入的编码并相应地处理它。 - jrochkind
1个回答

21
“兼容”这个词的含义并不十分明确,所以我们需要先了解一些基础知识。Unicode是底层概念,UTF-16和UTF-8是两种不同的编码方式。它们显然是不同的——否则,为什么会有两种不同的序列化格式呢?Unicode本身并没有指定序列化格式。UTF-8和UTF-16是两种备选的序列化格式。虽然它们都能表示相同的Unicode码点,但是它们的表示方式完全不同,因此是“不兼容”的,也是无法调和的。对于UTF-16,还有两个额外的细节。首先,实际上有两种不同的编码方式,UTF-16LE和UTF-16BE。它们在字节顺序方面有所不同(UTF-8是一种字节编码,因此没有字节顺序)。其次,旧版的UTF-16只支持65,536个可能的字符,这比Unicode目前包含的要少。这个问题通过代理项来解决,但是真正老旧或者有缺陷的UTF-16实现(正确地称为UCS-2而不是“真正”的UTF-16)不支持代理项。

为了更具体,让我们比较四个不同的代码点。 我们选择 U+0041, U+00E5, U+201C, 以及 U+1F4A9,因为它们很好地说明了差异。

U+0041是一个7位字符,因此UTF-8只需要使用一个字节来表示它。 U+00E5是一个8位字符,所以UTF-8需要对其进行编码。 U+1F4A9在基本多语言平面之外,因此UTF-16使用代理序列来表示它。 最后,U+201C不属于上述任何一种。

以下是我们候选字符在UTF-8、UTF-16LE和UTF-16BE中的表示。

字符 UTF-8 UTF-16LE UTF-16BE
U+0041 (a) 0x41 0x41 0x00 0x00 0x41
U+00E5 (å) 0xC3 0xA5 0xE5 0x00 0x00 0xE5
U+201C (“) 0xE2 0x80 0x9C 0x1C 0x20 0x20 0x1C
U+1F4A9 () 0xF0 0x9F 0x92 0xA9 0x3D 0xD8 0xA9 0xDC 0xD8 0x3D 0xDC 0xA9

举个显而易见的例子,UTF-8编码的U+00E5如果按照UTF-16解释,则代表完全不同的字符(在UTF-16LE中,它将是U+A5C3,而在UTF-16BE中则是U+C3A5)。任何由奇数个字节组成的UTF-8序列都是不完整的16位序列。我想当UTF-8被解释为UTF-16时,也可能会出现编码无效替代序列的情况。相反,许多UTF-16代码根本不是有效的UTF-8序列。因此,在这个意义上,UTF-8和UTF-16是完全不兼容的。

这些是字节值;在ASCII中,0x00是NUL字符(有时表示为^@),0x41是大写A,0xE5是未定义的;例如,在Latin-1中,它代表字符å(也方便地在Unicode中为U+00E5),但在KOI8-R中,它是Cyrillic字符Е(U+0415),等等
也许还要注意最后一个示例在UTF-16中需要进行非平凡的转换,使用一对代理代码点,在某种意义上类似于UTF-8如何编码所有多字节代码点。
在现代编程语言中,您的代码应该简单地使用Unicode,并让语言处理将其编码为适合您的平台和库的方式的细节问题。稍微离题一下,请参见http://utf8everywhere.org/

看了你链接的问题,那里的答案基本上告诉了你这个。我会提名关闭你的问题作为重复。 - tripleee
3
我非常不同意:问题“UTF-8是UTF-16的子集吗?”非常清楚,答案显然是“不是”。 - rasmus91
我强烈反对“简单使用Unicode”,因为数据共享可能需要重新编码,而不知道数据集的底层编码可能会以非常恼人和微妙的方式破坏应用程序。 - Jay-Pi
是的,有些情况下你需要理解编码以便于处理它们,但是如果你只是想处理文本,很多现代编程语言会抽象出内部表示的细节,让你专注于手头的任务。 - tripleee
@tripleee 抱歉,我不知道为什么要花费7年时间才正式接受你的答案...所以,顺便说一下,再次感谢;o) - mike

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