libiconv:在 UTF-8/UTF-16/UTF-32 之间转换时目标字节长度的安全估计?
libiconv: Safe estimation of target bytes-length when converting between UTF-8/UTF-16/UTF-32?
有大量关于 Unicode 代码单元、代码点等的信息,但我对使用字节流(libiconv 要求)转换组合字符、字素等仍然有点模糊。
目前我只对使用 libconv 的 iconv()
在 UTF-8/UTF-16/UTF-32 之间进行转换感兴趣,它需要源缓冲区和目标缓冲区的字节长度作为参数。
问题: 是否有一种安全的方法来快速计算目标缓冲区的最大可能字节长度,基于已经源缓冲区的已知字节长度?
比方说,使用已知的 u16byteslen
从 u16buf
转换为 u8buf
(不包括 0x0000 终止符,如果有的话)。在最坏的情况下,UTF-16 源缓冲区中每个代码点将有 1 个两字节单元,对应于 UTF-8 目标缓冲区中每个代码点有 4 个单字节单元。这足以安全地假设 UTF-8 目标缓冲区永远不会超过 2 * u16lenbytes
吗?
我实际上已经对此进行了试验并且似乎有效,但我不确定我是否遗漏了涉及组合字符和字素簇的极端情况。我的怀疑来自于我对这些东西是如何在这 3 种不同的编码中转换的无知。我的意思是,一个字素是否可能需要说 3 个 UTF-16 代码点,但在转换时需要 10 个 UTF-8 代码点?
在那种情况下,u16lenbytes
加倍是不够的,对吧?如果是这样,是否有任何其他直接的方法来预先计算目标缓冲区的最大长度?
Question: Is there a safe way to calculate fast the maximum possible bytes-length of the target buffer, based on the already known bytes-length of the source buffer?
是的。
to UTF-8
to UTF-16
to UTF-32
from UTF-8
×2
×4
from UTF-16
×1 ½
×1
from UTF-32
×1
×1
您可以通过按代码点范围对其进行分解来自行计算。选择一个源列和目标列,并找出最大的比率。
Code Point
UTF-8 length
UTF-16 length
UTF-32 length
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
。)
I mean, is it possible for a grapheme to need say 3 UTF-16 codepoints but like 10 UTF-8 codepoints when converted?
没有。这意味着某种其他类型的转换,如分解。 标量值输入的数量等于标量值输出的数量,加上可能 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 倍。对于 Chinese/Japanese/Korean,您位于代码点的此类区域。
有大量关于 Unicode 代码单元、代码点等的信息,但我对使用字节流(libiconv 要求)转换组合字符、字素等仍然有点模糊。
目前我只对使用 libconv 的 iconv()
在 UTF-8/UTF-16/UTF-32 之间进行转换感兴趣,它需要源缓冲区和目标缓冲区的字节长度作为参数。
问题: 是否有一种安全的方法来快速计算目标缓冲区的最大可能字节长度,基于已经源缓冲区的已知字节长度?
比方说,使用已知的 u16byteslen
从 u16buf
转换为 u8buf
(不包括 0x0000 终止符,如果有的话)。在最坏的情况下,UTF-16 源缓冲区中每个代码点将有 1 个两字节单元,对应于 UTF-8 目标缓冲区中每个代码点有 4 个单字节单元。这足以安全地假设 UTF-8 目标缓冲区永远不会超过 2 * u16lenbytes
吗?
我实际上已经对此进行了试验并且似乎有效,但我不确定我是否遗漏了涉及组合字符和字素簇的极端情况。我的怀疑来自于我对这些东西是如何在这 3 种不同的编码中转换的无知。我的意思是,一个字素是否可能需要说 3 个 UTF-16 代码点,但在转换时需要 10 个 UTF-8 代码点?
在那种情况下,u16lenbytes
加倍是不够的,对吧?如果是这样,是否有任何其他直接的方法来预先计算目标缓冲区的最大长度?
Question: Is there a safe way to calculate fast the maximum possible bytes-length of the target buffer, based on the already known bytes-length of the source buffer?
是的。
to UTF-8 | to UTF-16 | to UTF-32 | |
---|---|---|---|
from UTF-8 | ×2 | ×4 | |
from UTF-16 | ×1 ½ | ×1 | |
from UTF-32 | ×1 | ×1 |
您可以通过按代码点范围对其进行分解来自行计算。选择一个源列和目标列,并找出最大的比率。
Code Point | UTF-8 length | UTF-16 length | UTF-32 length |
---|---|---|---|
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
。)
I mean, is it possible for a grapheme to need say 3 UTF-16 codepoints but like 10 UTF-8 codepoints when converted?
没有。这意味着某种其他类型的转换,如分解。 标量值输入的数量等于标量值输出的数量,加上可能 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 倍。对于 Chinese/Japanese/Korean,您位于代码点的此类区域。