【问题标题】:Delphi StringBuilderDelphi 字符串生成器
【发布时间】:2009-03-26 16:05:13
【问题描述】:

在 Delphi 中存在类似 Java 或 C# StringBuilder 的东西吗?或者Delphi不需要StringBuilder,s := s + 'some string';是很好的表达方式(主要在for、while循环中)。

【问题讨论】:

标签: delphi


【解决方案1】:

是的,Delphi 提供 TStringBuilder(从 2009 版开始):

procedure TestStringBuilder;
var
  I: Integer;
  StringBuilder: TStringBuilder;
begin
  StringBuilder := TStringBuilder.Create;
  try
    for I := 1 to 10 do
    begin
      StringBuilder.Append('a string ');
      StringBuilder.Append(66); //add an integer
      StringBuilder.Append(sLineBreak); //add new line
    end;

    OutputWriteLine('Final string builder length: ' +
                    IntToStr(StringBuilder.Length));
  finally
    StringBuilder.Free;
  end;
end;

是的,你是对的。 s := s + 'text'; 并不比使用 TStringBuilder 慢。

【讨论】:

【解决方案2】:

在较旧的 Delphis 中,您可以使用 Hallvard Vassbotn 的 HVStringBuilder。我未能在他的博客上找到源代码,但您可以在 OmniThreadLibrary 源代码树中获取它们,例如(您需要文件 HVStringBuilder.pas 和 HVStringData.pas)。

【讨论】:

  • HVStringBuilder for olds Delphi 应用程序是一件好事!感谢您的回答。
  • 在获取 HVStringBuilder 的源代码之前,您能告诉我们这个库是否比 s:= s+ x 更快吗?非常感谢。
【解决方案3】:

Delphi 没有“REQUIRE”字符串构建器类,但如果您愿意使用它,Delphi 2009 提供了一个。您的 s := s + 'some string'; 示例是连接字符串的典型方法,在过去的几十年里一直在 Pascal/Delphi 中使用,没有任何重大问题。

【讨论】:

  • 如果您所做的只是处理字符串中的几十个字符,这可能是正确的。但是对于创建任何复杂的长字符串,其内存访问模式会阻止它就地重新分配,这是不正确的。此外,您的 Delphi 时间线似乎有点偏离 - 几十年???自 D2 以来的 13 年...
  • @mghie 包括 Pascal,我从 80 年代后期就开始编程。
  • mghie:有什么例子吗? Afaik 本机字符串生成器仅预分配比正常多一点的额外内存(由于堆分配粒度,正常字符串仅具有额外容量)。我想知道 tstringbuilder 是否会更快,直到您的字符串长度超过数千个字符。我可以找到关于这个主题的非常少的基准信息。
【解决方案4】:

提到的TStringBuilder 是要走的路。在您的特定情况下,串联可能没问题,但无论如何我都会尝试替代方案。

我正在内存中创建一个 EPUB 正文内容 xhtml 文件 (Delphi XE),生成它需要很长时间,以至于我从来没有让它完成(大约 5 分钟 加上放弃之前)。这是一个结合了大约 800,000 个字符的文本的真实示例。使用完全相同的代码并直接用 TStringBuilder.Append 语句替换 s:=s+'' 样式语句将其缩短到大约 3 秒。重申一下,除了从串联切换之外,没有任何逻辑变化。

【讨论】:

  • 有趣,我真的很想看看测试用例提供者。
【解决方案5】:

我在下面列出了一些关于 Delphi 字符串的好资源。

正如其他人所说,使用“+”运算符与通用字符串类型进行简单连接与使用 TStringbuilder 一样快(至少对于以下形式的操作:'s := s + [...]' )。不知道这是否属实,但性能至少足够接近,以至于下面的 [1] 断言“Delphi 中的字符串连接速度如此之快,以至于 Delphi 2009 中新优化的 StringBuilder 类无法击败它。”这是因为字符串被原地修改,如果需要,Delphi 会透明地为基本字符串分配更多内存,而不是将所有数据写入时复制操作到内存中的新位置。

[1]http://blog.marcocantu.com/blog/delphi_super_duper_strings.html

[2]http://conferences.codegear.com/he/article/32120

[3]http://www.codexterity.com/delphistrings.htm

[4]http://www.monien.net/blog/index.php/2008/10/delphi-2009-tstringbuilder/

【讨论】:

  • [1] 中的断言当然是废话,从 [4] 中的时序结果可以很容易地看出这一点。显然,自己的程序是否确实以差异重要的方式使用字符串构建是一个完全不同的问题。
  • 是的,我同意 [1] 中的断言可能是错误的。但我认为我不会仅仅从一个将单个空格迭代连接到基本字符串 10,000,000 次的基准测试就跳到这一点。谁知道观察到的速度差异是否会转化为特定的现实世界级联场景?
  • @Herbert:这个基准已经被选择为具有最小差异,因为在分配内存的连接之间没有做任何事情。在现实世界的场景中,就地内存重新分配通常是不可能的,因此 TStringBuilder 会更好。
  • @mghien FastMM 进行推测性重新分配。在实践中,TStringBuilder 并不是最好的选择,如果你使用多线程,它实际上很差,因为由于隐含的总线锁和临界区,它只会使用一个 CPU。
【解决方案6】:

我真的很惊讶,在任何 cmets 或示例中都没有提到您可以指示 TStringBuilder 在实例化期间预分配适合任务的缓冲区。换句话说,如果您可以对您可能需要多少内存进行简单的估计,稍微填充一下,并使用该值来实例化 TStringBuilder ,您就可以避免内存重新分配会减慢简单的字符串连接到爬行的速度:

buff := TStringBuilder.Create( tmpEstimatedSize );

我经常在新代码中使用 TStringBuilder 并优化旧代码,增量构建大字符串时的 CPU 节省非常显着。现在是透明的,如果我只有少数字符串要连接,我不会打扰 TStringBuilder。但是,如果我要序列化可能是大量数据的数据,那么 TStringBuilder 是显而易见的解决方案。

【讨论】:

    【解决方案7】:

    s := s + 'some string' 如果你在循环中执行它可能会非常慢,因为涉及到内存分配。我做了一些测试,表明预分配内存可以快 132 倍(是的,你没看错)!!!!

    代码是这样的:

     marker:= 1;
     CurBuffLen:= 0;
     for i:= 1 to Length(FileBody) DO
      begin
       if i > CurBuffLen then
        begin
         SetLength(s, CurBuffLen+ BuffSize);
         CurBuffLen:= Length(s)
        end;
       s[marker]:= FileBody[i];
       Inc(marker);
      end;
    

    详情请看我的回答:When and Why Should I Use TStringBuilder?

    注意:我的代码针对

    进行了优化

    s:= s+ c

    其中 c 是一个字符,但您可以轻松调整它

    for s:= s + '一些字符串'

    .

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多