【问题标题】:Characters that map to an odd number of bytes using Encoding.Unicode使用 Encoding.Unicode 映射到奇数字节的字符
【发布时间】:2019-01-29 07:38:21
【问题描述】:

我知道在某些情况下,某些字符在使用Encoding.Unicode.GetBytes() 时会占用更多空间。例如:

var value = Encoding.Unicode.GetBytes("????");,其中value.Length 为 4。

这不同于:

var value = Encoding.Unicode.GetBytes("a");,其中value.Length 为 2。

但是,我真的很好奇一些事情。是否有可能想出一组字符,使Encoding.Unicode.GetBytes() 返回奇数个字节,可能使用组合字符或其他方式?如果是这样,有人可以给我看一个这样的例子吗?如果不是,那是为什么呢?

【问题讨论】:

  • Encoding.Unicode 是 UTF-16LE 编码。代码单元是一个 16 位的值。
  • 有些Unicode编码可以有奇数个字节(最明显的例子是UTF8,简单的罗马字符(如'a')占一个字节,而三字节字符很多) . Wikipedia 上的 Unicode 条目对 Unicode 及其编码有很好的概述
  • @RaymondChen 旧事物 :) 谢谢,Raymond

标签: c# .net unicode encoding


【解决方案1】:

根据UTF-16,这些范围内的字符具有以下字节大小:

U+0000 到 U+D7FF:2 个字节。

U+E000 到 U+FFFF:2 个字节。

U+D800 到 U+DFFF:保留用于区分代理对(见下文)并且不应该编码,但是,尽管官方 Unicode 标准说没有 UTF 形式,包括 UTF-16,可以编码这些代码点,它在 C# 中仍然有效:var value = Encoding.Unicode.GetBytes("\uD800"); 另请注意,使用 var str = Encoding.Unicode.GetString(value); 将这些值转换回不会给您相同的结果!

U+10000 到 U+10FFFF:4 字节字符,带有两个使用上述保留范围的 2 字节代理对。 特别说明:虽然? 是一个字符,但它实际上是作为两个字符存储在 C# 字符串中的。以下情况属实:Debug.Assert("?".Length == 2);小心!

U+110000 及以上:不应该编码,而且似乎也不用 C# 编码。例如,这不起作用:var str = "\U00110000";

如果我们假设任何给定的字符分别占用 2 或 4 个字节(由于编码器的行为,我现在还不确定),那么通过简单的数学证明,两个偶数除以 2还是平的。偶数是 2 的倍数,写成:2m + 2n

上面的公式总是能被二整除,如(2m + 2n) / 2 = m + n

【讨论】:

  • "\U000110000" 实际上是 "\U00011000" (U+11000) 后跟 "0" (U+0030)。 \U 最多限制为 8 位数字。 U+11000 在 UTF-16 的有效代码点范围内。
  • @RemyLebeau 谢谢,我修改了我的答案。我有一个额外的 0。它似乎没有超出有效的 Unicode 范围进行编码。我说似乎是因为我还没有尝试过所有可能的 8 个字符十六进制代码点的组合。
  • UTF-16 在物理上被限制为最大代码点 U+10FFFF。它根本无法编码更高的代码点值。如果Encoding.Unicode 没有错误地接受更高的代码点,那么它有一个应该报告给 Microsoft 的错误。
  • @RemyLebeau 但是为什么保留字符(U+D800 到 U+DFFF)单独编码?
  • 因为它们仍然是 UTF-16 可以处理的 U+0000..U+10FFFF 范围内的有效代码点。它们只是保留代码点,在处理字符串中的代码点时应该永远未编码形式使用。
【解决方案2】:

我认为您可能对代码点和字节数或如何组合字符以创建字符串略有误导。有2**16 代码点,您可以枚举每个代码点以获取值并在需要确认时返回字节。

Unicode 有“平面”来描述字符(为了这个问题,假设 UTF-16,因为这些范围会随着 UTF-8 和 UTF-32 而变化)。字节的特定组合创建“对”,虽然它们仍然代表一个字符,但实际上占用 4 个字节来描述。

在 UTF-16 中,这些是代码点值高于 0x00FFFF 的字符,它们都是 4 个字节,而任何等于或小于的字符都将使用 2 个字节。正如我已经说过的,这只适用于 UTF-16。

因此,虽然字节数可能略有变化(尽管可以预见),但值将始终是偶数,它们将是 2 或 4。

【讨论】:

  • 我们同意?是一个字符吗?
  • 是的,一个“字符”需要 4 个字节来描述,因为 Unicode 使用了“平面”。任何代码点值高于0x00FFFF 的字符将是 4 个字节。更少的内容将是 2 个字节。这是假设我们仍在谈论 UTF-16。这可以随着 UTF-8 和 UTF-32 而改变。
  • 我认为您应该考虑更新答案中的措辞以包含代理对,用于可能编码为 4 个字节的字符。
  • 您混淆了代码点(U+000000 到 U+10FFFF)和代码单元(存储单元:对于 UTF-16,它是一个 16 位值)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-09
  • 1970-01-01
  • 2012-01-23
  • 2021-11-15
  • 2017-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多