delphi 我应该在什么时候以及为什么使用TStringBuilder?

qgelzfjb  于 2022-11-23  发布在  其他
关注(0)|答案(6)|浏览(307)

一年前,我将我的程序从 Delphi 4转换到Delphi 2009,主要是为了跳到Unicode,但也是为了获得Delphi多年改进的好处。
当然,我的代码都是遗留代码,它使用的短字符串现在都方便地变成了长的Unicode字符串,而且我把所有旧的ANSI函数都改成了新的等价函数。
但是在 Delphi 2009中,他们引入了TStringBuilder类,大概是模仿. NET的StringBuilder类。
我的程序进行了大量的字符串处理和操作,可以一次将数百兆字节的大字符串加载到内存中进行处理。
我不太了解 Delphi 的TStringBuilder实现,但我听说它的一些操作比使用默认的字符串操作更快。
我的问题是,对我来说,是否值得花费精力将我的标准字符串转换为使用TStringBuilder类,这样做有什么好处和损失?
感谢您的回答,并引导我得出我的结论,这是不麻烦,除非.NET兼容性是必需的。
在他的博客Delphi 2009 String Performance, Jolyon Smith states上:
但在我看来,TStringBuilder主要是作为一个.NET兼容性设备存在,而不是为Win32应用程序的开发人员提供任何真实的的好处,可能的例外是希望或需要单一源Win32/.NET代码库的开发人员,在那里字符串处理性能不是问题。

yvgpqqbh

yvgpqqbh1#

据我所知,TStringBuilder的引入只是为了与.NET和Java的一些对等,它似乎更多的是一个 * 勾选框 * 类型的功能,而不是任何主要的进步。
似乎一致认为TStringBuilder在某些操作中速度较快,但在其他操作中速度较慢。
您的程序听起来像是一个有趣的程序,可以与TStringBuilder进行前后比较,但我不会这样做,除非作为一个学术练习。

svmlkihl

svmlkihl2#

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

对于复杂的构建模式,第一种模式使我的代码更加清晰,第二种模式只有在我添加行并且通常包括许多Format调用时才能使用。
当格式模式很重要时,第三种方法使我的代码更清晰。
我只在表达式非常简单的时候才使用最后一个。
前两个成语之间还有一些不同之处:

  • TStringBuilder有许多Append的重载,如果您想像TStringList.Add那样添加行,则还可以使用AppendLine(只有两个重载
  • TStringBuilder使用超容量方案重新分配底层缓冲区,这意味着使用大缓冲区和频繁追加,它比TStringList快得多
  • 要获取TStringBuilder内容,必须调用ToString方法,这会降低速度。

所以:速度不是选择字符串附加习惯用法的最重要的因素,可读性才是。

u0sqgete

u0sqgete3#

我试图改进一个解析文本文件(1.5GB)的老例程。这个例程相当愚蠢,它构建了一个如下所示的字符串:s:= s+ buff[i];
所以,我以为TStringBuilder会增加显著的速度改进。结果它实际上慢了114%。
因此,我构建了自己的StringBuilder,它比经典的s:= s+ chr(在4 MB字符串上进行实验)快**184.82倍(是的,184倍!!!!!),甚至比TStringBuilder更快。

测试:
经典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;

预缓冲

Time:  
     BuffSize= 10000;       // 10k  buffer = 406ms
     BuffSize= 100000;      // 100k buffer = 140ms
     BuffSize= 1000000;     // 1M   buffer = 46ms

编码:

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;

预缓冲,作为类

Time:    
 BuffSize= 10000;       // 10k  buffer = 437ms       
 BuffSize= 100000;      // 100k buffer = 187ms        
 BuffSize= 1000000;     // 1M buffer = 78ms

编码:

procedure TfrmTester.btnBuffClassClick(Sender: TObject);
VAR
   StringBuff: TCStringBuff;
   s: string;
   FileBody: string;
   c: Cardinal;
   i: Integer;
begin
 FileBody:= ReadFile(File4MB);
 c:= GetTickCount;

 StringBuff:= TCStringBuff.Create(BuffSize);
 TRY
   for i:= 1 to Length(FileBody) DO
    StringBuff.AddChar(filebody[i]);
   s:= StringBuff.GetResult;
 FINALLY
  FreeAndNil(StringBuff);
 END;

 Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms');
 if s <> FileBody
 then Log.Lines.Add('FAILED!');
end;

这是一个类:

{ TCStringBuff }

constructor TCStringBuff.Create(aBuffSize: Integer= 10000);
begin
 BuffSize:= aBuffSize;
 marker:= 1;
 CurBuffLen:= 0;
 inp:= 1;
end;

function TCStringBuff.GetResult: string;
begin
 SetLength(s, marker-1);                    { Cut down the prealocated buffer that we haven't used }
 Result:= s;
 s:= '';         { Free memory }
end;

procedure TCStringBuff.AddChar(Ch: Char);
begin
 if inp > CurBuffLen then
  begin
   SetLength(s, CurBuffLen+ BuffSize);
   CurBuffLen:= Length(s)
  end;

 s[marker]:= Ch;
 Inc(marker);
 Inc(inp);
end;

结论:
如果你有很大的字符串(超过10 K),请停止使用s:= s + c。即使你有很小的字符串,但你经常这样做(例如,你有一个函数,它正在对一个小字符串进行字符串处理,但你经常调用它)。

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

tct7dpnv

tct7dpnv4#

TStringBuilder的引入只是为了为应用程序提供源代码兼容机制,以便在** Delphi Delphi.NET中执行字符串处理。您可以牺牲Delphi中的一些速度,以获得Delphi.NET中的一些潜在的重要好处
.NET中的
StringBuilder概念解决了该平台上字符串实现的性能问题,而 Delphi **(本机代码)平台根本没有这些问题。

如果您编写的代码不需要针对本机代码和** Delphi 进行编译,那么就没有理由使用TStringBuilder**。

fumotvh3

fumotvh35#

根据Marco Cantu不是为了速度,但是你可能会得到更干净的代码和更好的代码兼容性。Here(和一些更正here)另一个速度测试与TStringBuilder不是更快。

huwehgph

huwehgph6#

正如LachlanG所说,TStringBuilder基本上只是一个模仿功能。.NET需要它是因为CLR字符串是不可变的,但 Delphi 没有这个问题,所以它并不真正需要字符串构建器作为解决方案。

相关问题