如何解决libiconv:在 UTF-8/UTF-16/UTF-32 之间转换时安全估计目标字节长度?
有大量关于 Unicode 代码单元、代码点等的信息,但我对使用字节流(libiconv 要求)转换组合字符、字素等仍然有些模糊。
目前我只对使用 libconv 的 iconv()
在 UTF-8/UTF-16/UTF-32 之间转换感兴趣,它需要源缓冲区和目标缓冲区的字节长度作为参数。
问题:是否有一种安全的方法可以根据源缓冲区的已知字节长度快速计算目标缓冲区的最大可能字节长度?
例如,假设使用已知的 u16buf
(不包括 0x0000 终止(如果有))从 u8buf
转换为 u16byteslen
。在最坏的情况下,UTF-16 源缓冲区中的每个代码点将有 1 个两字节单元,对应于 UTF-8 目标缓冲区中的每个代码点 4 个单字节单元。这是否足以安全地假设 UTF-8 目标缓冲区的长度永远不会超过 2 * u16lenbytes
?
我实际上已经对此进行了试验并且似乎有效,但我不确定我是否遗漏了涉及组合字符和字素簇的极端情况。我的怀疑来自于我对这些东西如何在这 3 种不同编码之间转换的无知。我的意思是,字素是否可能需要说 3 个 UTF-16 代码点,但在转换时需要 10 个 UTF-8 代码点?
在那种情况下,将 u16lenbytes
加倍是不够的,对吧?如果是这样,有没有其他直接的方法来预先计算目标缓冲区的最大长度?
解决方法
问题:是否有一种安全的方法可以根据源缓冲区的已知字节长度快速计算目标缓冲区的最大可能字节长度?
是的。
到 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-16BE
或 UTF-16LE
。)
我的意思是,一个字素是否可能需要说 3 个 UTF-16 代码点,但在转换时需要 10 个 UTF-8 代码点?
没有。这将意味着某种其他类型的转换,例如分解。 标量值输入的数量等于标量值输出的数量,在可能添加U+FEFF字节顺序标记开始。 (我说“标量值”而不是“代码点”,因为“标量值”不包括代理。如果您对可能有错误或可能是垃圾数据的文本进行转码,它不会改变结果的大小。)
,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 / 4
或 3 * len / 2
。
因此,如果在第一个公式中我们分配了双倍字节(如您所想),那么在第二个公式中,最大值仅为字节数的 1.5 倍。对于中文/日文/韩文,您位于代码点的此类区域。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。