给定 UTF-16 大小的最大 UTF-8 字符串大小

Maximum UTF-8 string size given UTF-16 size

确定编码给定数量的 UTF-16 代码单元所需的最大 UTF-8 字节数的公式是什么(即 C#/.NET 中 String.Length 的值)?

我看到了 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-16 在单个代码单元中对 BMP 中的所有字符进行编码,而所有其他平面都在 2 个代码单元中进行编码。

似乎UTF-8可以将整个BMP编码在3个字节以内,而使用4个字节来编码其他平面。

因此在我看来,上面的选项 (2) 是正确的答案,这应该可行:

string str = "Some string";
int maxUtf8EncodedSize = str.Length * 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 编码转换的最大长度。 String Length 衡量字符总数,即 UTF-16 代码点的计数。因此,作为最坏情况的估计,我相信您可以安全地估计 count(Char) * 3,因为非 BMP 字符将 count(Char) * 2 产生 4 个字节作为 UTF-8。

如果你想获得表示的 UTF-32 代码点的总数,你应该能够做类似的事情

var maximumUtf8Bytes = System.Globalization.StringInfo(myString).LengthInTextElements * 4;

(我的 C# 有点生疏,因为我在过去几年没有使用过太多 .Net 环境,但我认为它可以解决问题)。

单个 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,这会将长度为 2N 字节的 UTF-16 字符串转换为长度为 3[=26 的 UTF-8 字符串=]N字节。

所以是的,你是对的。我认为您不需要考虑像字形这样的东西,因为这类事情是在从 UTF8/16 解码到代码点之后完成的。