在分析过程中,我遇到了一个函数,它花费了相当多的时间,但本质上可以归结为这个非常简单的代码:
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
Result := Copy(AInput, AStart, ASubstringLength);
end;
这个函数返回预期的子字符串,但在处理更长的输入时性能不佳。我查看了CPU视图中的汇编代码,并从中可以看出(通常我不是在汇编级别工作),似乎在调用Copy之前将AInput隐式转换为字符串。
但由于此时未知字符串/字符数组的长度,所以转换代码必须遍历PChar的长度,直到找到空终止符。这就解释了对于更长输入的可怕扩展性。
然而,由于调用者传递了PChar的长度,最初我认为可以将方法转换为使用SetString。
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
SetString(Result, AInput + AStart - 1, ASubstringLength);
end;
除了
SetString
使用零索引(而不是像 Copy 一样使用基于一的索引)之外,似乎还有许多其他小事情要在验证输入方面做 Copy
,其中并非所有都有记录(例如,任何小于1的起始值都会更改为1)。因此,上面的天真实现并不总是像原始实现那样工作。我的目标是尽可能地复制
Copy
程序,因为该函数是库的一部分,并且已经被我的同事广泛使用。我想知道以下实现是否完成了这一目标,或者是否需要注意
Copy
的任何其他注意事项。注意:FLength
是来自该功能所属模块中的另一个部分的 AInput
的实际长度。我为了这个例子而删除了那个其他部分。function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
if (AInput = nil) then begin
Result := '';
end else begin
if (AStart < 1) then begin
AStart := 0;
end else begin
AStart := AStart - 1;
end;
if (ASubstringLength + AStart > FLength) then begin
ASubstringLength := FLength - AStart;
end;
SetString(Result, AInput + AStart, ASubstringLength);
end;
end;
我正在使用Delphi 2006,但我认为在产品的其他版本中(至少是非Unicode版本)并没有太大区别。
GetSubString()
进行内联。 - LU RDSetString()
是从零开始计数(而不是像Copy()
一样从一开始计数)之外,它还没有索引。它是基于起始指针而不是索引操作的。Copy()
有一个以1为基础的索引参数,因此原始的GetSubstring()
也有一个以1为基础的AStart
。你最初修改的GetSubstring()
正在计算错误的指针。它需要使用-1
而不是+1
来保持与Copy()
相同的语义(不包括角落情况):SetString(Result, Ainput + (AStart - 1), ASubstringLength);
- Remy Lebeau