Unicode 字符串上的 Length() 与 Sizeof()

Length() vs Sizeof() on Unicode strings

引用 Delphi XE8 帮助:

For single-byte and multibyte strings, Length returns the number of bytes used by the string. Example for UTF-8:

   Writeln(Length(Utf8String('1¢'))); // displays 3

For Unicode (WideString) strings, Length returns the number of bytes divided by two.

这里出现了重要的问题:

  1. 为什么处理方式完全不同?
  2. 为什么 Length() 没有按预期执行,return 只是参数的长度(如元素的数量)而不是在某些情况下以字节为单位给出大小?
  3. 为什么它声明将 Unicode (UTF-16) 字符串的结果除以 2? AFAIK UTF-16 最多为 4 个字节,因此这将给出不正确的结果。

Length returns 将字符串视为数组时的元素个数。

  • 对于 8 位元素类型(ANSI、UTF-8)的字符串,Length 给出字节数,因为字节数与元素数相同。
  • 对于具有 16 位元素 (UTF-16) 的字符串,Length 是字节数的一半,因为每个元素的宽度为 2 个字节。

您的字符串“1¢”有两个代码点,但第二个代码点需要两个字节才能以 UTF-8 编码。因此 Length(Utf8String('1¢')) 的计算结果为三。

您在问题标题中提到了 SizeOf。将字符串变量传递给 SizeOf 将始终 return 指针的大小,因为字符串变量在幕后只是一个指针。

针对您的具体问题:

Why the difference in handling is there at all?

如果您将 Length 视为与字节相关,则只有区别。但这是错误的思考方式 Length 始终 return 是一个元素计数,并且当以这种方式查看时,所有字符串类型以及所有数组类型的行为都是统一的。

Why Length() doesn't do what it's expected to do, return just the length of the parameter (as in, the count of elements) instead of giving the size in bytes in some cases?

它总是 return 元素计数。恰好当元素大小为单个字节时,元素计数和字节计数恰好相同。事实上,您引用的文档还包含以下内容,就在您提供的摘录之上:Returns 字符串中的字符数或数组中的元素数。 那是关键文本。您包含的摘录是为了说明这个斜体文本的含义。

Why does it state it divides the result by 2 for Unicode (UTF-16) strings? AFAIK UTF-16 is 4-byte at most, and thus this will give incorrect results.

UTF-16 字符元素始终为 16 位宽。但是,某些 Unicode 代码点需要两个字符元素进行编码。这些字符元素对称为代理项对。


我认为,您希望 Length 将 return 字符串中的代码点数。但事实并非如此。它 returns 字符元素的数量。而对于可变长度编码,代码点的数量不一定与字符元素的数量相同。如果您的字符串被编码为 UTF-32,那么代码点的数量将与字符元素的数量相同,因为 UTF-32 是一种恒定大小的编码。

计算代码点的一种快速方法是扫描字符串检查代理项对。当您遇到代理对时,计算一个代码点。否则,当您遇到不属于代理项对的字符元素时,计算一个代码点。在 pseudo-code:

N := 0;
for C in S do
  if C.IsSurrogate then
    inc(N)
  else
    inc(N, 2);
CodePointCount := N div 2;

另一点是代码点数与可见字符数不同。一些代码点正在组合字符并与其相邻的代码点组合以形成单个可见字符或字形。

最后,如果您只想找到字符串有效负载的字节大小,请使用此表达式:

Length(S) * SizeOf(S[1])

此表达式适用于所有类型的字符串。

要非常小心函数 System.SysUtils.ByteLength。从表面上看,这似乎正是您想要的。但是,该函数 returns 是 UTF-16 编码字符串的字节长度。因此,如果您将 AnsiString 传递给它,那么 ByteLength 编辑的值 return 是 AnsiString.

的字节数的两倍