1

有大量关于 Unicode 代码单元、代码点等的信息,但我仍然对使用字节流(libiconv要求)转换组合字符、字素等有点模糊。

目前我只对使用 libconv's 在 UTF-8/UTF-16/UTF-32 之间转换感兴趣iconv(),它期望源缓冲区和目标缓冲区的字节长度作为参数。

问题:是否有一种安全的方法可以根据源缓冲区的已知字节长度快速计算目标缓冲区的最大可能字节长度?

比方说,例如,从已知转换u16buf为(不包括 0x0000-termination,如果有的话)。在最坏的情况下,UTF-16 源缓冲区中每个代码点将有 1 个两字节单元,对应于 UTF-8 目标缓冲区中每个代码点有 4 个单字节单元。这足以安全地假设 UTF-8 目标缓冲区永远不会长于吗?u8bufu16byteslen2 * u16lenbytes

我实际上已经对此进行了试验并且似乎可以工作,但是我不确定我是否缺少涉及组合字符和字素簇的极端案例。我的怀疑来自于我对这些东西如何在这 3 种不同的编码之间进行转换的无知。我的意思是,一个字形是否可能需要说 3 个 UTF-16 代码点,但在转换时需要 10 个 UTF-8 代码点?

在那种情况下,翻倍u16lenbytes是不够的,对吧?如果是这样,是否有任何其他直接的方法来预先计算目标缓冲区的最大长度?

4

2 回答 2

6

问题:是否有一种安全的方法可以根据源缓冲区的已知字节长度快速计算目标缓冲区的最大可能字节长度?

是的。

转为 UTF-8 转为 UTF-16 转为 UTF-32
来自 UTF-8 ×2 ×4
来自 UTF-16 ×1 ½ ×1
来自 UTF-32 ×1 ×1

您可以通过按代码点范围将其分解来自己计算。选择一个源和目标列,并找到最大的比率。

代码点 UTF-8 长度 UTF-16 长度 UTF-32 长度
0000…007F 1 2 4
0080…07FF 2 2 4
0800…FFFF 3 2 4
10000…10FFFF 4 4 4

组合字符和字素簇不会影响任何事情。编码只是将一系列Unicode 标量值转换为字节,而且它们非常简单。

请注意,在转换为 UTF-16 时需要额外添加两个字节,在转换为 UTF-32 时需要额外添加四个字节,因为这些编码会在文本开头添加 BOM U+FEFF。(如果您不希望这样,请使用一种无​​ BOM 编码,例如UTF-16BEUTF-16LE。)

我的意思是,一个字形是否可能需要说 3 个 UTF-16 代码点,但在转换时需要 10 个 UTF-8 代码点?

不,这意味着某种其他类型的转换,例如分解。输入的标量值的数量等于输出的标量值的数量,开头可能会添加 U+FEFF 字节顺序标记。(我说“标量值”而不是“代码点”,因为“标量值”不包括代理项。如果您正在转码可能有错误或可能是垃圾数据的文本,它不会改变结果的大小。)

于 2021-06-04T14:07:32.807 回答
2

Unicode 代码点可以被编码:

  • UTF-8:1、2、3 或 4 个字节
  • UTF-16:2 或 4 个字节
  • UTF-32:4 个字节
  • (已过时):UCS-2:2 个字节(但对于某些代码点,它需要两个代理项)。

所以,作为初步估计,如果你有 UTF-16 字节的长度,你可以使用这样的公式是安全的:

byte_len_utf8 = 4 * byte_len_utf16 / 2

但这不是一个好方法:我们更清楚:只有 UTF-16 是 4 字节长度时,UTF-8 才是 4 字节长度。所以我们有两种情况:4 * len / 43 * len / 2

因此,如果在第一个公式中我们分配双倍字节(如您所料),在第二个公式中,最大值仅为字节数的 1.5 倍。对于中文/日文/韩文,您位于代码点的此类区域。

于 2021-06-04T14:13:40.303 回答