我何时以及为什么要使用TStringBuilder?

lke*_*ler 15 delphi unicode stringbuilder delphi-2009

一年前我将程序从Delphi 4转换为Delphi 2009,主要是为了跳转到Unicode,同时也获得了Delphi多年来改进的好处.

我的代码当然是所有遗留代码.它使用短字符串,现在已经很方便地变成了长Unicode字符串,并且我已经将所有旧的ANSI函数更改为新的等效函数.

但是在Delphi 2009中,他们引入了TStringBuilder类,可能是在StringBuilder类.NET之后建模的.

我的程序执行了大量的字符串处理和操作,并且可以同时将数百兆字节的大字符串加载到内存中.

我不太了解Delphi的TStringBuilder实现,但我听说它的一些操作比使用默认字符串操作更快.

我的问题是我是否值得通过努力并将我的标准字符串转换为使用TStringBuilder类.这样做我会得到什么和失去什么?


感谢您的回答并引导我得出结论,除非需要.NET兼容性,否则不要打扰.

在他关于Delphi 2009 String Performance的博客中,Jolyon Smith说:

但它看起来好像TStringBuilder主要作为.NET兼容性工具,而不是为Win32应用程序的开发人员提供任何真正的好处,可能除了希望或需要单一来源Win32/.NET代码库的开发人员字符串处理性能不是一个问题.

Lac*_*anG 13

据我所知,TStringBuilder只是为了与.NET和Java的一些奇偶校验而引入,它似乎更像是一个盒子类型功能而不是任何重大进步.

共识似乎是TStringBuilder在某些操作中更快,但在其他操作中更慢.

你的程序在TStringBuilder比较之前/之后听起来像一个有趣的程序,但除了作为学术练习之外我不会这样做.


Jer*_*ers 11

基本上,我使用这些习语来构建字符串.最重要的区别是:

对于复杂的构建模式,第一个使我的代码更清晰,第二个只有我添加行并且通常包括许多Format调用.

当格式模式很重要时,第三个使代码更清晰.

我只在表达式非常简单时才使用最后一个.

前两个成语之间的一些差异:

  • TStringBuilder有许多重载Append,如果你想添加像can 这样的行,也有AppendLine(只有两个重载)TStringList.Add
  • TStringBuilder 使用超容量方案重新分配底层缓冲区,这意味着使用大缓冲区和频繁附加,它可能比 TStringList
  • 要获取TStringBuilder内容,您必须调用ToString方法,这可能会减慢速度.

所以:速度不是选择你的字符串附加成语最重要的事情.可读代码是.


WeG*_*ars 9

我试图改进一个解析文本文件(1.5GB)的旧例程.例程非常愚蠢,它正在构建一个这样的字符串:Result:= Result+ buff[i];

所以我认为TStringBuilder会显着提高速度.事实证明,'dumb'代码实际上比使用TStringBuilder的'改进'版本快114%.

因此,从字符构建字符串不是一个可以通过TStringBuilder获得速度提升的地方.


我的StringBuilder(下面)比经典的s:= s + chr快184.82倍(是184 !!!!!!).(4MB字符串的实验)

经典s:= s + c
时间:8502毫秒

procedure TfrmTester.btnClassicClick(Sender: TObject);
VAR
   s: string;
   FileBody: string;
   c: Cardinal;
   i: Integer;
begin
 FileBody:= ReadFile(File4MB);
 c:= GetTickCount;
 for i:= 1 to Length(FileBody) DO
  s:= s+ FileBody[i];
 Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms');     // 8502 ms
end;
Run Code Online (Sandbox Code Playgroud)

Prebuffered
时间:
BUFFSIZE = 10000; // 10k buffer = 406ms BuffSize = 100000; // 100k buffer = 140ms BuffSize = 1000000; // 1M缓冲区= 46ms

代码:
procedure TfrmTester.btnBufferedClick(Sender:TObject); VAR s:string; FileBody:string; c:红衣主教; CurBuffLen,marker,i:整数; 开始FileBody:= ReadFile(File4MB); c:= GetTickCount;

Time:  
     BuffSize= 10000;       // 10k  buffer = 406ms
     BuffSize= 100000;      // 100k buffer = 140ms
     BuffSize= 1000000;     // 1M   buffer = 46ms
Run Code Online (Sandbox Code Playgroud)

预补充,如类
Time:
BuffSize = 10000; // 10k buffer = 437ms
BuffSize = 100000; // 100k buffer = 187ms
BuffSize = 1000000; // 1M缓冲区= 78ms

代码:
procedure TfrmTester.btnBuffClassClick(Sender:TObject); VAR StringBuff:TCStringBuff; s:字符串; FileBody:string; c:红衣主教; 我:整数; 开始FileBody:= ReadFile(File4MB); c:= GetTickCount;

procedure TfrmTester.btnBufferedClick(Sender: TObject);
VAR
   s: string;
   FileBody: string;
   c: Cardinal;
   CurBuffLen, marker, i: Integer;
begin
 FileBody:= ReadFile(File4MB);
 c:= GetTickCount;

 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;

 SetLength(s, marker-1); { Cut down the prealocated buffer that we haven't used }  
 Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms');
 if s <> FileBody
 then Log.Lines.Add('FAILED!');
end;
Run Code Online (Sandbox Code Playgroud)

这是班级:

Time:    
 BuffSize= 10000;       // 10k  buffer = 437ms       
 BuffSize= 100000;      // 100k buffer = 187ms        
 BuffSize= 1000000;     // 1M buffer = 78ms     
Run Code Online (Sandbox Code Playgroud)

结论:如果您有大(超过10K)字符串,请停止使用s:= s + c.即使你有小字符串也可能是这样,但你经常这样做(例如,你有一个函数在一个小字符串上做一些字符串处理,但你经常调用它)._

PS:你可能也想看到这个:https://www.delphitools.info/2013/10/30/efficient-string-building-in-delphi/2/

  • 感谢您添加这个迟来的答案。这是您链接到的一篇优秀文章。 (2认同)

Del*_*ics 8

TStringBuilder的引入仅仅是为了在DelphiDelphi.NET中执行字符串处理的应用程序提供源代码兼容机制.您在Delphi中牺牲了一些速度,以获得Delphi.NET中一些潜在的显着优势

.NET中StringBuilder概念解决了该平台上字符串实现的性能问题,这是Delphi(本机代码)平台根本没有的问题.

如果您没有编写需要为本机代码和Delphi.NET编译的代码,那么就没有理由使用TStringBuilder.

  • @IanH Mike Lischke的VirtualTrees在Embarcadero之前推出了一个`TStringBuilder`.它是用unicode字符串创建的加速操作.唯一的支持来自Window的BSTR,Delphi将其作为"WideString"公开.Windows无法*重新分配*BSTR(BSTR未被引用计数;`SysReallocString`创建第二个字符串并执行复制).在这种情况下,Delphi`TStringBuilder`是必需的.之后,如果要添加很多东西,在字符串上设置`Capacity`的功能非常有用.另外,这是一个很好的语法. (4认同)
  • 它不仅仅是为了源代码兼容性而引入的.这是它的一部分,但另一个强有力的理由是它是一个强大的类使用,因为有些人更喜欢它能够做流畅的编码模式.底线 - 如果您愿意,可以使用它,如果您不想要,请不要使用它. (2认同)

Lar*_*ens 7

根据Marco Cantu的说法,不是为了提高速度,但是你可能会获得更清晰的代码以及与.Net更好的代码兼容性.这里(以及这里的一些更正)使用TStringBuilder的另一个速度测试不是更快.

  • 谢谢你的链接.他们帮助了.但我不能同意你的看法,StringBuilder提供了更清晰的代码.我当然喜欢:s:= s + s2; 优于:SB.Append(s2); (5认同)

Mas*_*ler 6

TStringBuilder基本上只是一个me-too功能,就像LachlanG所说的那样.它在.NET中是必需的,因为CLR字符串是不可变的,但是Delphi没有那个问题所以它并不需要字符串构建器作为变通方法.