Pregunta

¿Alguien ha escrito una rutina 'UnFormat' para Delphi?

Lo que estoy imaginando es el inverso de SysUtils.Formato y se parece a esto

UnFormat('un número %n y otro %n',[float1, float2]);

Entonces podrías descomprimir una cadena en una serie de variables usando cadenas de formato.

Miré la rutina 'Formato' en SysUtils, pero nunca usé ensamblador, por lo que no tiene sentido para mí.

¿Fue útil?

Solución

Esto se llama scanf en C, he creado un parecido a Delphi para esto:

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;

Otros consejos

Sé que tiende a asustar a la gente, pero podrías escribir una función simple para hacer esto usando expresiones regulares.

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

Si le preocupan las expresiones regulares, eche un vistazo a www.regexbuddy.com y nunca mirarás atrás.

Tiendo a encargarme de esto usando un analizador simple.Tengo dos funciones, una se llama NumStringParts que devuelve el número de "partes" de una cadena con un delimitador específico (en su caso encima del espacio) y GetStrPart devuelve la parte específica de una cadena con un delimitador específico.Ambas rutinas se han utilizado desde mis días de Turbo Pascal en muchos proyectos.

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;

Ejemplo de uso:

 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; 

Estas rutinas también funcionan de maravilla para cadenas delimitadas por comas simples o estrictas.Estas rutinas funcionan de maravilla en Delphi 2009/2010.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top