我需要将一个字符串拆分为一个带有固定长度子字符串的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:
数据大小:2805KB,FixedLen:100:
答案 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)
您的代码中可能会有一些明显的优化:
不要修改源字符串,只需提取所需的字符串即可 子。然后,您可以使输入字符串参数const, 允许编译器进一步优化对过程的调用
因为你正在处理固定长度和输入字符串 已知长度,那么你可以预先计算所需的容量 字符串列表并在添加列表时避免内存重新分配 到。
我将如何解决这个问题:
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
,直到结束。