在Delphi中将字符串拆分为固定长度的快速方法

时间:2015-08-11 13:26:09

标签: delphi delphi-2006

我需要将一个字符串拆分为一个带有固定长度子字符串的TStringList。

目前我使用:

procedure StrToStringList(ASource: string; AList: TStrings; AFixedLen: Integer);
begin
    Assert(Assigned(AList));
    while Length(ASource) > AFixedLen do
    begin
        AList.Add(LeftStr(ASource, AFixedLen));
        Delete(ASource, 1, AFixedLen);
    end;
    AList.Add(ASource);
end;

这有效,但似乎很慢。有更好/更快的想法吗?

已修改:结果分析

速度增益令人印象深刻。 以下是我(主观)剖析的结果。

数据大小:290KB,FixedLen:100:

  • 原始代码:58 ms
  • Heffernan:1 ms
  • Deltics:1 ms

数据大小:2805KB,FixedLen:100:

  • 原始代码:5803 ms
  • Heffernan:5毫秒
  • Deltics:4毫秒

3 个答案:

答案 0 :(得分:7)

我认为修改输入字符串是浪费的。避免这样:

var
  Remaining: Integer;
  StartIndex: Integer;
begin
  Remaining := Length(ASource);
  StartIndex := 1;
  while Remaining > 0 do
  begin
    AList.Add(Copy(ASource, StartIndex, AFixedLen));
    inc(StartIndex, AFixedLen);
    dec(Remaining, AFixedLen);
  end;
end;

这将减少堆分配和复制的数量。

但是,如果你观察到性能上没什么好处,我也不会感到惊讶。为了执行任何严肃的优化,我们可能需要查看一些示例输入数据。

答案 1 :(得分:5)

您的代码中可能会有一些明显的优化:

  1. 不要修改源字符串,只需提取所需的字符串即可 子。然后,您可以使输入字符串参数const, 允许编译器进一步优化对过程的调用

  2. 因为你正在处理固定长度和输入字符串 已知长度,那么你可以预先计算所需的容量 字符串列表并在添加列表时避免内存重新分配 到。

  3. 我将如何解决这个问题:

    procedure StrToStringList(const aSource: String;
                              const aList: TStrings;
                              const aFixedLen: Integer);
    var
      idx: Integer;
      srcLen: Integer;
    begin
      aList.Capacity := (Length(aSource) div aFixedLen) + 1;
    
      idx    := 1;
      srcLen := Length(aSource);
    
      while idx <= srcLen do
      begin
        aList.Add(Copy(aSource, idx, aFixedLen));
        Inc(idx, aFixedLen);
      end;
    end;
    

    此处的容量计算可能导致容量过剩1,其中输入字符串精确地除以固定长度,但这是一个可忽略的开销imho,并且在目标是最佳性能的情况下是可接受的(替代方案是修改的条件分支)或者以不同的方式计算容量以满足可能少数情况下的容量。)

答案 2 :(得分:2)

不要在循环中使用delete。它会导致整个字符串移动。使用基于integer的索引变量从1开始,并在使用AFixedLen提取子字符串之后每次增加copy,直到结束。