【问题标题】:C: Most efficient way to determine how many bytes will be needed for a UTF-16 string from a UTF-8 stringC:从 UTF-8 字符串中确定 UTF-16 字符串需要多少字节的最有效方法
【发布时间】:2011-08-09 08:10:37
【问题描述】:

我已经看到了一些非常聪明的代码,用于在 Unicode 代码点和 UTF-8 之间进行转换,所以我想知道是否有人拥有(或喜欢设计)这个。

  • 给定一个 UTF-8 字符串,相同字符串的 UTF-16 编码需要多少字节。
  • 假设 UTF-8 字符串已经过验证。它没有 BOM,没有过长的序列,没有无效的序列,是空终止的。不是CESU-8
  • 必须支持带代理的完整 UTF-16。

具体来说,我想知道是否有捷径可以知道何时需要代理对,而无需将 UTF-8 序列完全转换为代码点。

我见过的最好的 UTF-8 到代码点代码使用矢量化技术,所以我想知道这是否也可以在这里。

【问题讨论】:

  • “无无效序列”是否包括“无 UTF-8 编码的代理值”?
  • @Joachim:是的。 “它不是 CESU-8”。
  • 啊,我明白了,我不知道这个词。
  • @Karl:因为我想要一个理想的低级 C 解决方案。我想鼓励对系统函数调用的指针。特别是我想要类似的东西:daemonology.net/blog/2008-06-05-faster-utf8-strlen.html(但也欢迎讨论其他语言或独立于语言的想法)

标签: c algorithm utf-8 utf-16 unicode-string


【解决方案1】:

非常简单:计算头字节数,重复计算字节数F0 及以上。

在代码中:

size_t count(unsigned char *s)
{
    size_t l;
    for (l=0; *s; s++) l+=(*s-0x80U>=0x40)+(*s>=0xf0);
    return l;
}

注意:此函数以 UTF-16 代码单元返回长度。如果您想要所需的字节数,请乘以 2。如果您要存储一个空终止符,您还需要为此考虑空间(一个额外的代码单元/两个额外的字节)。

【讨论】:

  • +1:我不认为你的代码很简单,但它非常简洁,当我用纸检查你的位操作时,一切似乎都是正确的.只有一个迂腐的错误。您的函数返回 16 位代码单元的数量而不是字节数(这是问题所要求的)。返回 l * 2 可以解决这个问题。
  • 确实,我没有注意到 OP 要求提供字节。
【解决方案2】:

效率始终是速度与规模的权衡。如果速度比大小更受青睐,那么最有效的方法就是根据源字符串的长度进行猜测。

有4种情况需要考虑,简单取最坏的情况作为最终的缓冲区大小:

  • U+0000-U+007F - 在 utf8 中编码为 1 个字节,在 utf16 中每个字符编码为 2 个字节。 (1:2 = x2)
  • U+0080-U+07FF - 编码为 2 字节 utf8 序列,或每个字符 2 字节 utf16 字符。 (2:2 = x1)
  • U+0800-U+FFFF - 存储为 3 字节 utf8 序列,但仍适合单个 utf16 字符。 (3:2 = x.67)
  • U+10000-U+10FFFF - 存储为 4 字节 utf8 序列,或 utf16 中的代理对。 (4:4 = x1)

最坏的情况扩展因素是在将 U+0000-U+007f 从 utf8 转换为 utf16 时:缓冲区,按字节计算,只需要是源字符串的两倍大。当编码为 utf16 为 utf8 时,每个其他 unicode 代码点都会产生相同的大小或较小的字节分配。

【讨论】:

  • “最有效”很大程度上取决于如何设置计算成本与内存成本的优先级,但我同意:总的来说,这很可能是最好的方法。
  • 我想到了这一点,但没有完全分析它。现在你拥有它确实看起来非常简单。谢谢!
  • 我推迟接受你的回答,因为我希望有人能想出一个聪明的矢量化算法。您的方法在时间上显然是无与伦比的,但最坏的情况是,对于不使用 ASCII 以外的许多字符的语言,使用的内存量接近两倍。
【解决方案3】:

这不是算法,但如果我理解正确,规则是这样的:

  • MSB 为0 的每个字节增加 2 个字节(1 个 UTF-16 代码单元)
    • 该字节表示 U+0000 - U+007F 范围内的单个 Unicode 代码点
  • 具有 MSB 1101110 的每个字节增加 2 个字节(1 个 UTF-16 代码单元)
    • 这些字节分别以 2 和 3 字节序列开始,它们代表 U+0080 - U+FFFF 范围内的 Unicode 代码点
  • 每个具有 4 个 MSB 集的字节(即以 1111 开头)增加 4 个字节(2 个 UTF-16 代码单元)
    • 这些字节开始 4 字节序列,涵盖 Unicode 范围的“其余部分”,可以用 UTF-16 中的低和高代理表示
  • 可以跳过所有其他字节(即以10 开头的字节)
    • 这些字节已与其他字节一起计算。

我不是 C 专家,但这看起来很容易矢量化。

【讨论】:

  • 看起来不错,但您提到了几年前从合法 UTF-8 中删除的“5 或 6 字节序列”,而您没有提到 4 字节序列。
  • 缺少的 4 是一个错字(它应该与 5s 和 6s 一起分组),并且不再允许 5/6 字节序列的事实并没有真正改变算法:它们只是用 4 字节序列分组,无论如何都会产生相同的结果。 (即我用“4字节”替换“5或6字节”;-))
  • 啊哈,它看起来像 Unicode 范围 >= U+10000 确实是 UTF-8 4 字节范围和 Supplementary Planes 的开始,这是代理覆盖的内容。一个完美的捷径 (-:
猜你喜欢
  • 2016-05-31
  • 1970-01-01
  • 2014-02-05
  • 2016-09-07
  • 2014-01-19
  • 2011-09-06
  • 2012-01-20
  • 2019-07-30
  • 2017-03-04
相关资源
最近更新 更多