如何使此代码更快?该字符串可以包含“,。?#”等字符,也可以包含其他字符。
Const Nums = ['0'..'9'];
function CleanNumber(s: String): Int64;
Var z: Cardinal;
begin
for z := length(s) downto 1 do
if not (s[z] in Nums) then Delete(s,z,1);
if s = '' then
Result := 0 else
Result := StrToInt64(s);
end;
结果(长循环): CL2,CL3 = HeartWare的
32位,“脏号码” /“干净号码”
64位,“脏号码” /“干净号码”
答案 0 :(得分:3)
这里有两个示例,它们肯定比您拥有的示例要快(从字符串中删除字符相对较慢):
此方法的工作原理是预先分配一个最大可能长度的字符串,然后在源字符串中遇到数字时用数字填充。不会删除每个不受支持的字符,也不会扩展每个受支持字符的目标字符串。
FUNCTION CleanNumber(CONST S : STRING) : Int64;
VAR
I,J : Cardinal;
C : CHAR;
T : STRING;
BEGIN
SetLength(T,LENGTH(S));
J:=LOW(T);
FOR I:=LOW(S) TO HIGH(S) DO BEGIN
C:=S[I];
IF (C>='0') AND (C<='9') THEN BEGIN
T[J]:=C;
INC(J)
END
END;
IF J=LOW(T) THEN
Result:=0
ELSE BEGIN
SetLength(T,J-LOW(T)); // or T[J]:=#0 [implementation-specific]
Result:=StrToInt64(T)
END
END;
通过将最终结果乘以10并加上相应的数字值来工作。
{$IFOPT Q+}
{$DEFINE OverflowEnabled }
{$ELSE }
{$Q+ If you want overflow checking }
{$ENDIF }
FUNCTION CleanNumber(CONST S : STRING) : Int64;
VAR
I : Cardinal;
C : CHAR;
BEGIN
Result:=0;
FOR I:=LOW(S) TO HIGH(S) DO BEGIN
C:=S[I];
IF (C>='0') AND (C<='9') THEN Result:=Result*10+(ORD(C)-ORD('0'))
END
END;
{$IFNDEF OverflowEnabled } {$Q-} {$ENDIF }
{$UNDEF OverflowEnabled }
还请注意,我不使用IN或CharInSet,因为它们比简单的内联> =和<=比较要慢得多。
我可以提出的另一条评论是在字符串变量上使用LOW和HIGH。这使它与基于0的字符串(移动编译器)和基于1的字符串(桌面编译器)兼容。
答案 1 :(得分:2)
您的功能变慢主要是因为Delete
方法。每次调用Delete
时,都需要移动很多字符。
一种更快的方法是这样的:
function DirtyStrToNum(const S: string): Int64;
var
tmp: string;
i, j: Integer;
const
DIGITS = ['0'..'9'];
begin
SetLength(tmp, S.Length);
j := 0;
for i := 1 to S.Length do
if CharInSet(S[i], DIGITS) then
begin
Inc(j);
tmp[j] := S[i];
end;
SetLength(tmp, j);
if tmp.IsEmpty then
Result := 0
else
Result := StrToInt64(tmp);
// Or, but not equivalent: Result := StrToInt64Def(tmp, 0);
end;
请注意,我为新字符串进行了一次分配,然后仅将最少数量的字符复制到其中。