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

时间:2015-06-03 12:13:59

标签: delphi delphi-xe8

引用Delphi XE8帮助:

  

对于单字节和多字节字符串,Length返回字符串使用的字节数。 UTF-8的示例:

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

对于Unicode(WideString)字符串,Length返回字节数除以2。

这引起了一些重要问题:

  1. 为什么处理方面存在差异?
  2. 为什么Length()没有做预期的事情,只返回参数的长度(如元素的数量),而不是在某些情况下给出字节大小?
  3. 为什么它声明它将结果除以2(Unicode(UTF-16)字符串? AFAIK UTF-16最多为4字节,因此会产生错误的结果。

1 个答案:

答案 0 :(得分:8)

Length在将字符串视为数组时返回元素数。

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

你的字符串'1¢'有两个代码点,但第二个代码点需要两个字节来用UTF-8编码。因此Length(Utf8String('1¢'))评估为三。

您在问题标题中提到SizeOf。将字符串变量传递给SizeOf将始终返回指针的大小,因为字符串变量在引擎盖下只是一个指针。

针对您的具体问题:

  

为什么处理上存在差异?

如果您认为Length与字节有关,则只有区别。但这是考虑它的错误方式Length总是返回一个元素计数,当以这种方式查看时,所有字符串类型的行为都是统一的,实际上所有数组类型都是一致的。

  

为什么Length()没有做预期的事情,只返回参数的长度(如元素的数量),而不是在某些情况下给出字节大小?

它总是返回元素计数。恰好是当元素大小是单个字节时,元素计数和字节计数恰好相同。实际上,您引用的文档也包含您提供的摘录上方的以下内容:返回字符串中的字符数或数组中的元素。这是关键文本。您所包含的摘录旨在说明此斜体文本的含义。

  

为什么它声明它将结果除以2(Unicode(UTF-16)字符串? AFAIK UTF-16最多为4字节,因此会产生错误的结果。

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

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

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

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要非常小心。从表面上看,这似乎就是你想要的。但是,该函数返回UTF-16编码字符串的字节长度。因此,如果您传递AnsiString,那么ByteLength返回的值是AnsiString的字节数的两倍。