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

Fast way to split a string into fixed-length parts in Delphi

我需要将字符串拆分为具有固定长度子字符串的 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,固定长度:100:

数据大小:2805KB,固定长度:100:

我认为修改输入字符串很浪费。像这样避免这种情况:

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;

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

但是,如果您观察到性能几乎没有提高,我也不会感到惊讶。为了执行任何认真的优化,我们可能需要查看一些示例输入数据。

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

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

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

  2. 由于您处理的是固定长度和输入字符串 已知长度,则可以预先计算出所需的容量 字符串列表并避免在添加列表时重新分配内存 到.

以下是我的处理方式:

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,其中输入字符串正好除以固定长度,但这是一个可以忽略不计的开销恕我直言,并且在目标是最佳性能的情况下是可以接受的(替代方案是条件分支以不同方式修改或计算容量,以满足可能是少数情况的情况)。