Dephi:将“脏”字符串转换为数字的更快方法

时间:2019-05-20 05:18:35

标签: 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 := StrToInt64(s);
end;

结果(长循环): CL2,CL3 = HeartWare的

32位,“脏号码” /“干净号码”

  • 矿井:270ms,165ms
  • CL2:220ms,210ms
  • CL3: 100ms ,110ms
  • DirtyStrToNum:215ms, 90ms

64位,“脏号码” /“干净号码”

  • 地雷:2280ms,75ms
  • CL2:1320ms,130ms
  • CL3: 280毫秒 25毫秒
  • DirtyStrToNum:1390ms,125ms

2 个答案:

答案 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;

请注意,我为新字符串进行了一次分配,然后仅将最少数量的字符复制到其中。