Delphi Dephi:将“脏”字符串转换为数字的更快方法
如何使代码更快?字符串可以包含诸如、.?之类的字符。?可能还有其他人Delphi Dephi:将“脏”字符串转换为数字的更快方法,delphi,Delphi,如何使代码更快?字符串可以包含诸如、.?之类的字符。?可能还有其他人 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
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=心脏器具的
32位,脏号/干净号
矿井:270ms,165ms
CL2:220ms,210ms
CL3:100ms,110ms
dirtystrotnum:215ms,90ms
64位,脏号/干净号
地雷:2280毫秒,75毫秒
CL2:1320ms,130ms
CL3:280ms,25ms
dirtystrotnum:1390ms,125ms
你的功能很慢,主要是因为删除方法。每次调用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;
请注意,我为一个新字符串进行了一次分配,然后只向其中复制最小数量的字符。您的函数运行缓慢,主要是因为采用了删除方法。每次调用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;
请注意,我只为一个新字符串进行了一次分配,然后只向其中复制了最少数量的字符。这里有两个例子肯定比从字符串中删除字符相对较慢的例子要快: 这个方法的工作原理是预先分配一个最大可能长度的字符串,然后在源字符串中遇到数字时用数字填充它。不删除每个不支持的字符,不扩展每个支持的字符的目标字符串
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,因为它们比简单的内联>=和要慢得多。这里有两个示例,它们肯定比从字符串中删除字符相对较慢的示例要快: 这个方法的工作原理是预先分配一个最大可能长度的字符串,然后在源字符串中遇到数字时用数字填充它。不删除每个不支持的字符,不扩展每个支持的字符的目标字符串
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,因为它们比简单的inline>=慢得多,并且每次delete语句都会重新分配一个新字符串。只需使用for循环并构建一个只包含数字的新字符串。虽然下面的答案回答了您的实际问题,但我还想指出,在某些情况下,使用这样的函数可能不是一个好主意。如果一个错误或用户错误使街道地址填充了一个变量/DB字段/编辑字段,该变量/DB字段/编辑字段应该用于数量,那么您可能希望显示一条错误消息,而不是静默地接受街道地址Storgatan 5作为数量5。如果您有特定格式(如123-4566)的代码,请为该精确格式编写解析器。您的delete语句将每次重新分配一个新字符串。只需使用for循环并构建一个只包含数字的新字符串。虽然下面的答案回答了您的实际问题,但我还想指出,在某些情况下,使用这样的函数可能不是一个好主意。如果一个错误或用户错误使街道地址填充了一个变量/DB字段/编辑字段,该变量/DB字段/编辑字段应该用于数量,那么您可能希望显示一条错误消息,而不是静默地接受街道地址Storgatan 5作为数量5。如果您有特定格式(如123-4566)的代码,请为该精确格式编写解析器。这在没有任何堆分配的情况下是可能的。@DavidHeffernan:事实上,确实有可能进一步改进。没有任何Delphi rtl函数可以从固定长度字符数组转换为整数或其他类型。rtl迫使我们将堆用于此类任务,这非常令人沮丧。我目前正在为充满数字的简单文件优化一个文本文件解析器。让rtl坚持使用堆会让人恼火。强制我编写自己的函数来执行转换。我只看到1个堆分配。截断tmp变量不一定会导致堆重新分配,但很可能只会根据内存管理器和初始分配的特定长度更新字符串记录中的长度字段。同样,两次堆分配。这在没有任何堆分配的情况下是可能的。@DavidHeffernan:事实上,确实有可能进一步改进。没有任何Delphi rtl函数可以从固定长度字符数组转换为整数或其他类型。rtl迫使我们将堆用于此类任务,这非常令人沮丧。我目前正在为充满数字的简单文件优化一个文本文件解析器。让rtl坚持使用堆会让人恼火。强制我编写自己的函数来执行转换。我只看到1个堆分配。不需要截断tmp变量
可能会导致堆重新分配,但很可能只会根据内存管理器和初始分配的特定长度更新字符串记录中的长度字段;此对话已结束。评论不用于扩展讨论;这段对话已经结束。