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

Delphi Unicode字符串上的Length()与Sizeof(),delphi,delphi-xe8,Delphi,Delphi Xe8,引用Delphi XE8帮助: 对于单字节和多字节字符串,Length返回字符串使用的字节数。UTF-8的示例: Writeln(Length(Utf8String('1¢'))); // displays 3 对于Unicode(WideString)字符串,Length返回字节数除以2 这就产生了一些重要问题: 为什么在处理上会有差异呢 为什么Length()不执行预期的操作,在某些情况下只返回参数的长度(如元素计数),而不是以字节为单位给出大小 为什么它会声明它将Unicode(U

引用Delphi XE8帮助:

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

   Writeln(Length(Utf8String('1¢'))); // displays 3
对于Unicode(WideString)字符串,Length返回字节数除以2

这就产生了一些重要问题:

  • 为什么在处理上会有差异呢
  • 为什么Length()不执行预期的操作,在某些情况下只返回参数的长度(如元素计数),而不是以字节为单位给出大小
  • 为什么它会声明它将Unicode(UTF-16)字符串的结果除以2?AFAIK UTF-16最多为4字节,因此这将给出不正确的结果

  • Length
    返回将字符串视为数组时的元素数

    • 对于具有8位元素类型(ANSI、UTF-8)的字符串,
      Length
      提供字节数,因为字节数与元素数相同
    • 对于具有16位元素(UTF-16)的字符串,则
      长度
      是字节数的一半,因为每个元素的宽度为2字节
    字符串“1■”有两个代码点,但第二个代码点需要两个字节才能用UTF-8编码。因此
    长度(Utf8String('1×'))
    计算为三

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

    对于您的具体问题:

    为什么在处理上会有差异呢

    如果将
    长度
    视为与字节相关,则两者之间只有一个区别。但这是错误的想法,因为
    Length
    总是返回一个元素计数,这样看时,所有字符串类型以及所有数组类型的行为都是一致的

    为什么Length()不执行预期的操作,在某些情况下只返回参数的长度(如元素计数),而不是以字节为单位给出大小

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

    为什么它会声明它将Unicode(UTF-16)字符串的结果除以2?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])
    
    此表达式适用于所有类型的字符串


    对函数要非常小心。从表面上看,这似乎正是你想要的。但是,该函数返回UTF-16编码字符串的字节长度。因此,如果你给它传递一个
    AnsiString
    ,那么
    bytellength
    返回的值是
    AnsiString
    的字节数的两倍看看我问题中的代码。“1■”只有两个字符长,但输出仍然是3。@ZzZombo没错。您有两个代码点,但UTF-8编码字节数组的长度为3。
    length(s)*SizeOf(s[1])
    提供字符串占用的字节数。@ZzZombo不要这样做!这将涉及无意义的堆分配。使用我之前评论中的简单表达式。否。空字符串也可以。然后返回
    0
    SizeOf()
    在编译时进行计算。try
    lenibytes:=Length(UTF8Encode('ССааа'))
    var u8:UTF8String;u8:=“Сааа”;I:=Length(u8)
    -不带typecastI写这篇文章是因为它是用编码的名字写的,编码一个字符需要多少钱。我把它和另一种编码搞混了。我还原了你所有的编辑。主要是因为我不想一直更新我的答案来匹配!;-)不管怎样,我想很明显你现在已经掌握了一切。这个问题很好。我们不能让它保持原样吗。