确定编码给定数量的UTF-16代码单元(即C#/ .NET中String.Length
的值)所需的最大UTF-8字节数的公式是什么?
我看到3种可能性:
1)# of UTF-16 code units x 2
2)# of UTF-16 code units x 3
3)# of UTF-16 code units x 4
UTF-16代码点由1或2个代码单元表示,因此我们只需要考虑一个字符串充满一个或另一个字符串的最坏情况。如果UTF-16字符串完全由2个代码单元代码点组成,那么我们知道UTF-8表示的大小最多相同,因为这两种表示形式的代码点最多占用4个字节,因此最坏的情况是是上面的选项(1)。
因此,我不知道答案的有趣案例是,单个代码单元UTF-16代码点在UTF-8表示形式中可能需要的最大字节数。
如果所有单个代码单元的UTF-16代码点都可以用3个UTF-8字节表示,这是我的直觉告诉我的最合理的话,那么选项(2)将是最坏的情况。如果有任何需要4个字节的文件,那么选项(3)将是答案。
有人知道哪个正确吗?我真的希望(1)或(2),因为(3)会使事情变得更加困难:/
更新
我距离UTF专家还很远,但据我所知,UTF-16将BMP中的所有字符编码为一个代码单元,而所有其他平面均以2个代码单元编码。
UTF-8似乎可以在3个字节内对整个BMP进行编码,并使用4个字节对其他平面进行编码。
因此,在我看来,上面的选项(2)是正确的答案,这应该可行:
string str = "Some string";
int maxUtf8EncodedSize = str.Length * 3;
看起来好像已经退房了吗?
答案 0 :(得分:3)
格式正确的UTF-8每个Unicode代码点最多可以包含4个字节。
UTF-16编码的字符每个Unicode代码点最多可以包含2个16位序列。
基本多语言平面之外的字符(包括添加到最新版本的Unicode中的表情符号和语言)最多以21位表示,这在UTF-8格式中导致4个字节的序列,结果也在UTF-16中占用4个字节。
但是,有些环境做事很奇怪。由于基本多语言平面之外的UTF-16字符最多需要2个16位序列(它们是可检测的,因为它们始终是U + D800到U + DFFF范围内的16位序列),因此一些错误的UTF-8实现,通常称为CESU-8,它将这些UTF-8序列转换为两个3字节的UTF-8序列,每个UTF-32码点总共六个字节。 (我相信某些早期的Oracle DB实现可以做到这一点,而且我相信它们并不是唯一的实现。)
事情还有另外一个小扳手,那就是某些字形被归类为组合字符,并且在确定屏幕上显示的内容时使用了多个UTF-16(或UTF-32)序列,但是我没有认为这适用于您的情况。
根据您的编辑,您似乎正在尝试估计.Net编码转换的最大长度。字符串长度用于测量字符总数,它是UTF-16代码点的计数。因此,作为最坏情况的估计,我相信您可以放心地估计count(Char)* 3,因为非BMP字符将是count(Char)* 2,产生4个字节的UTF-8。
如果要获取表示的UTF-32代码点总数,则应该可以执行类似的操作
var maximumUtf8Bytes = System.Globalization.StringInfo(myString).LengthInTextElements * 4;
(我的C#有点生疏,因为最近几年我没有使用.Net环境太多,但是我认为可以解决问题)。
答案 1 :(得分:1)
单个UTF-16单词的最坏情况是U+FFFF
,它在UTF-16中按原样(0xFFFF)Cyberchef编码。在UTF-8中,它被编码为ef bf bf
(三个字节)。
两个UTF-16单词(一个“代理对”)的最坏情况是U+10FFFF
,在UTF-16中将其编码为0xDBFF DFFF
。在UTF-8中,它被编码为f3 cf bf bf
(四个字节)。
因此,最坏的情况是加载U+FFFF
,这会将长度为2 * N字节的UTF-16字符串转换为长度为3 * N字节的UTF-8字符串。
是的,你是正确的。我认为您不需要考虑字形之类的东西,因为这种事情是在从UTF8 / 16解码到代码点之后完成的。