【问题标题】:Is there an inverse function of *SysUtils.Format* in DelphiDelphi中是否有*SysUtils.Format*的反函数
【发布时间】:2010-09-09 12:23:55
【问题描述】:

有人为 Delphi 编写过“UnFormat”例程吗?

我想象的是 SysUtils.Formatinverse,看起来像这样

UnFormat('一个数字 %n 和另一个 %n',[float1, float2]);

因此,您可以使用格式字符串将字符串解压缩为一系列变量。

我查看过 SysUtils 中的“格式化”例程,但我从未使用过汇编,所以它对我来说毫无意义。

【问题讨论】:

    标签: delphi function scanf


    【解决方案1】:

    这在 C 中称为 scanf,我为此做了一个 Delphi 外观:

    function ScanFormat(const Input, Format: string; Args: array of Pointer): Integer;
    var
      InputOffset: Integer;
      FormatOffset: Integer;
      InputChar: Char;
      FormatChar: Char;
    
      function _GetInputChar: Char;
      begin
        if InputOffset <= Length(Input) then
        begin
          Result := Input[InputOffset];
          Inc(InputOffset);
        end
        else
          Result := #0;
      end;
    
      function _PeekFormatChar: Char;
      begin
        if FormatOffset <= Length(Format) then
          Result := Format[FormatOffset]
        else
          Result := #0;
      end;
    
      function _GetFormatChar: Char;
      begin
        Result := _PeekFormatChar;
        if Result <> #0 then
          Inc(FormatOffset);
      end;
    
      function _ScanInputString(const Arg: Pointer = nil): string;
      var
        EndChar: Char;
      begin
        Result := '';
        EndChar := _PeekFormatChar;
        InputChar := _GetInputChar;
        while (InputChar > ' ')
          and (InputChar <> EndChar) do
        begin
          Result := Result + InputChar;
          InputChar := _GetInputChar;
        end;
    
        if InputChar <> #0 then
          Dec(InputOffset);
    
        if Assigned(Arg) then
          PString(Arg)^ := Result;
      end;
    
      function _ScanInputInteger(const Arg: Pointer): Boolean;
      var
        Value: string;
      begin
        Value := _ScanInputString;
        Result := TryStrToInt(Value, {out} PInteger(Arg)^);
      end;
    
      procedure _Raise;
      begin
        raise EConvertError.CreateFmt('Unknown ScanFormat character : "%s"!', [FormatChar]);
      end;
    
    begin
      Result := 0;
      InputOffset := 1;
      FormatOffset := 1;
      FormatChar := _GetFormatChar;
      while FormatChar <> #0 do
      begin
        if FormatChar <> '%' then
        begin
          InputChar := _GetInputChar;
          if (InputChar = #0)
          or (FormatChar <> InputChar) then
            Exit;
        end
        else
        begin
          FormatChar := _GetFormatChar;
          case FormatChar of
            '%':
              if _GetInputChar <> '%' then
                Exit;
            's':
              begin
                _ScanInputString(Args[Result]);
                Inc(Result);
              end;
            'd', 'u':
              begin
                if not _ScanInputInteger(Args[Result]) then
                  Exit;
    
                Inc(Result);
              end;
          else
            _Raise;
          end;
        end;
    
        FormatChar := _GetFormatChar;
      end;
    end;
    

    【讨论】:

    • 谢谢。正是我需要的。用法示例:ScanFormat('number 27 string Hello','number %d string %s',[@anInt,@aString]);注意:如果字符串后面的第一个字符也包含在字符串本身中,则无法提取字符串。
    【解决方案2】:

    我倾向于使用简单的解析器来处理这个问题。我有两个函数,一个称为 NumStringParts ,它返回具有特定分隔符的字符串中“部分”的数量(在您的情况下,在空格上方),GetStrPart 从具有特定分隔符的字符串中返回特定部分。自从我在 Turbo Pascal 时代以来,这两个例程都已在许多项目中使用。

    function NumStringParts(SourceStr,Delimiter:String):Integer;
    var
      offset : integer;
      curnum : integer;
    begin
      curnum := 1;
      offset := 1;
      while (offset <> 0) do
        begin
          Offset := Pos(Delimiter,SourceStr);
          if Offset <> 0 then
            begin
              Inc(CurNum);
                Delete(SourceStr,1,(Offset-1)+Length(Delimiter));
            end;
        end;
      result := CurNum;
    end;
    
    function GetStringPart(SourceStr,Delimiter:String;Num:Integer):string;
    var
      offset : integer;
      CurNum : integer;
      CurPart : String;
    begin
      CurNum := 1;
      Offset := 1;
      While (CurNum <= Num) and (Offset <> 0) do
        begin
          Offset := Pos(Delimiter,SourceStr);
          if Offset <> 0 then
            begin
              CurPart := Copy(SourceStr,1,Offset-1);
              Delete(SourceStr,1,(Offset-1)+Length(Delimiter));
              Inc(CurNum)
            end
          else
            CurPart := SourceStr;
        end;
      if CurNum >= Num then
        Result := CurPart
      else
        Result := '';
    end;
    

    使用示例:

     var
        st : string;
        f1,f2 : double; 
      begin
         st := 'a number 12.35 and another 13.415';
         ShowMessage('Total String parts = '+IntToStr(NumStringParts(st,#32)));
         f1 := StrToFloatDef(GetStringPart(st,#32,3),0.0);
         f2 := StrToFloatDef(GetStringPart(st,#32,6),0.0);
         ShowMessage('Float 1 = '+FloatToStr(F1)+' and Float 2 = '+FloatToStr(F2)); 
      end; 
    

    这些例程也适用于简单或严格的逗号分隔字符串。这些例程在 Delphi 2009/2010 中运行良好。

    【讨论】:

      【解决方案3】:

      我知道它往往会吓到人,但你可以使用正则表达式编写一个简单的函数来做到这一点

      'a number (.*?) and another (.*?)
      

      如果您担心 reg 表达式,请查看 www.regexbuddy.com,您将永远不会回头。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-09-08
        • 1970-01-01
        • 2011-09-11
        • 2014-10-12
        • 1970-01-01
        • 2016-10-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多