【问题标题】:There is an ansi version of StrToInt?StrToInt 有 ansi 版本吗?
【发布时间】:2014-06-16 13:04:13
【问题描述】:

StrToInt 似乎没有 Ansi 重载。这是正确的吗?或者,也许我错过了一些东西。 StrToInt 坚持将我的 ansistrings 转换为字符串。

【问题讨论】:

  • 你可以试试 FastCoder 的 asm 实现。请参阅 fastcode.sourceforge.net/challenge_content/… 和 FastCodeStrToInt32Unit.pas 文件。将字符串类型更改为 AnsiString。
  • shlwapi.StrToIntA 似乎没有调用 StrToIntW 或 StrToIntExW。虽然不知道性能..

标签: delphi


【解决方案1】:

你是对的。 StrToInt 没有 ANSI 版本。找到标准函数的 ANSI 版本的地方是 AnsiStrings 单元,那里什么都没有。

要么编写自己的函数来完成这项工作,要么接受使用StrToInt 所需的转换。

编写自己的函数并不难。它可能看起来像这样:

uses 
  SysConst; // for SInvalidInteger
....
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
function AnsiStrToInt(const s: AnsiString): Integer;

  procedure Error;
  begin
    raise EConvertError.CreateResFmt(@SInvalidInteger, [s]);
  end;

var
  Index, Len, Digit: Integer;
  Negative: Boolean;
begin
  Index := 1;
  Result := 0;
  Negative := False;
  Len := Length(s);
  while (Index <= Len) and (s[Index] = ' ') do
    inc(Index);
  if Index > Len then
    Error;
  case s[Index] of
  '-','+':
    begin
      Negative := s[Index] = '-';
      inc(Index);
      if Index > Len then
        Error;
    end;
  end;
  while Index <= Len do
  begin
    Digit := ord(s[Index]) - ord('0');
    if (Digit < 0) or (Digit > 9) then
      Error;
    Result := Result * 10 + Digit;
    if Result < 0 then
      Error;
    inc(Index);
  end;
  if Negative then
    Result := -Result;
end;

这是StrToInt 中的精简版。它不处理十六进制,并且对错误更加严格。在使用此代码之前,我想测试这是否真的是您的瓶颈。

非常有趣的是,这段基于 RTL 源代码的代码无法返回 low(Integer)。修复它并不难,但它会使代码更复杂。

【讨论】:

  • 性能会那么差。当然,您的程序是受磁盘限制的,而不是受 CPU 限制的。你测量了吗?
  • @Altar:但是您只进行一次 StrToInt 转换,对吗?所以这不是瓶颈。磁盘 I/O 将成为比您的计算更大的瓶颈。
  • 您是否真的尝试过使用像 David 写的函数(我认为十六进制对您来说并不重要,无论如何?)并实际分析哪个更快,或者是否存在瓶颈以及在哪里存在瓶颈?如果您的计算如此复杂以至于磁盘 I/O 不再是瓶颈,那么转换字符串的 ISTM 也可能不是瓶颈。
  • @Altar 如果你真的有一个瓶颈,那么我准备打赌它会在堆分配上。将分隔符上的行拆分为单独的值,导致堆分配的船负载。它们将比 text 到 int 的转换至少占一个数量级的性能。我确实强烈怀疑您没有处于真正的性能瓶颈之上。
  • 你真正需要的是一个函数,它将一行作为输入并返回一个值数组。更好的是,您传入一个预先分配的数组,如果可能的话,最好在堆栈上,并且函数一次拆分和填充,而不接触堆。请记住,当您想要方便时,堆是您的朋友,但当您关心性能时,堆是您的死敌。
【解决方案2】:

代码实际上很简单(不支持十六进制字符串,但你不需要它们):

function AnsiStrToInt(const S: RawByteString): Integer;
var
  P: PByte;
  Negative: Boolean;
  Digit: Integer;

begin
  P:= Pointer(S);
// skip leading spaces
  while (P^ = Ord(' ')) do Inc(P);
  Negative:= False;
  if (P^ = Ord('-')) then begin
    Negative:= True;
    Inc(P);
  end
  else if (P^ = Ord('+')) then Inc(P);

  if P^ = 0 then
    raise Exception.Create('No data');

  Result:= 0;
  repeat
    if Cardinal(Result) > Cardinal(High(Result) div 10) then
      raise Exception.Create('Integer overflow');
    Digit:= P^ - Ord('0');
    if (Digit < 0) or (Digit > 9) then
      raise Exception.Create('Invalid char');
    Result:= Result * 10 + Digit;
    if (Result < 0) then begin
      if not Negative or (Cardinal(Result) <> Cardinal(Low(Result))) then
        raise Exception.Create('Integer overflow');
    end;
    Inc(P);
  until (P^ = 0);
  if Negative then Result:= -Result;
end;

【讨论】:

  • Tmp &lt; 0 可能更有效。或者将Result * 10 + Digit 直接分配给Result 并检查&lt; 0。有趣的是,整个方法意味着该函数无法返回low(Integer)。我想它需要为 +ve 和 -ve 使用不同的分支才能返回low(Integer)
  • 不,其实第二个想法是错误的,我删了;反例是9999999999
  • 我修复了Low(Integer)的情况。
  • 你确定吗?因为if Negative then Result:= -Result; 看起来它不能产生low(Integer)。那是因为abs(low(Integer)) = abs(high(Integer)) + 1
  • 试试Writeln(AnsiStrToInt(IntToStr(Low(Integer))));
【解决方案3】:

我遵循了这个提示:

How to convert AnsiString to UnicodeString in Delphi XE4

例子:

var
  a : AnsiString;
  b : String;
  c : Integer;
begin
  a := '123';
  b := String(a);
  c := StrToInt(b);

【讨论】:

  • 并不是这里讨论的内容的真正答案。有关详细信息,请参阅 Davids 的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-17
  • 2012-12-07
  • 2011-06-16
  • 2010-11-28
  • 1970-01-01
  • 2013-11-15
  • 2010-09-22
相关资源
最近更新 更多