Что-то вроде StrTok () или Sscanf ()? - PullRequest
3 голосов
/ 09 февраля 2011

Итак, я читаю из ModBos через последовательный порт и получаю показания примерно такие: '+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003';

По сути, всегда будет 8 значений с плавающей запятой, перед которыми стоит знак плюс или минус, хотя они могут иметь различную длину символов.

Какой самый эффективный способ получить значения в массиве с плавающей точкой (или массиве строк или TSringList)?

Я не уверен, но это может быть критично ко времени, поэтому эффективность, вероятно, превосходит элегантность.

Ответы [ 5 ]

8 голосов
/ 09 февраля 2011

Я бы сделал что-то вроде этого:

type
  TFloatArray = array[0..7] of Double;

procedure ParseFloats(const aFloatStr: string;
  var aFloatArray: TFloatArray);
var
  lPos: Integer;
  lNextPos: Integer;
  lPosPositive: Integer;
  lPosNegative: Integer;
  i: Integer;
  lFormatSettings: TFormatSettings;
begin
  //do not forget formatsettings, or you will get problems with regional settings
  lFormatSettings.DecimalSeparator := '.';
  lFormatSettings.ThousandSeparator := ',';
  lPos := 1;
  for i := 0 to High(aFloatArray) do
  begin
    lPosPositive := PosEx('+', aFloatStr, lPos + 1);
    lPosNegative := PosEx('-', aFloatStr, lPos + 1);
    if lPosPositive = 0 then
      lNextPos := lPosNegative
    else if lPosNegative = 0 then
      lNextPos := lPosPositive
    else
      lNextPos := Min(lPosPositive, lPosNegative);
    if lNextPos = 0 then
      lNextPos := Length(aFloatStr) + 1;
    aFloatArray[i] := StrToFloat(Copy(aFloatStr, lPos, lNextPos - lPos), lFormatSettings);
    lPos := lNextPos;
  end;
end;

//call like this
var
  lFloats: TFloatArray;
begin
  ParseFloats('+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003', lFloats);
end;

Поскольку всегда есть 8 значений с плавающей запятой, достаточно фиксированного массива из 8 значений типа double.Я сводил манипуляции со строками к минимуму, только один раз для каждого значения с плавающей запятой копируется строка.Важным является TFormatSettings, иначе вы получите ошибки в системах, где десятичный разделитель не является точкой (как у меня).

Здесь нет обработки исключений, я ожидаю строку с 8 значениями с плавающей запятой, ничего болеене меньше.

6 голосов
/ 09 февраля 2011

Вы можете скачать и использовать VC ++ sscanf , портированный на Delphi.

4 голосов
/ 09 февраля 2011

вы можете использовать класс TParser для анализа вашей строки.

проверьте этот пример приложения

program ParserDemo;

{$APPTYPE CONSOLE}

uses
  Classes,
  SysUtils;

procedure ProcessModBosOutPut(OutPut : string);
var
 StringStream  : TStringStream;
 Parser        : TParser;
 dValue        : Double;
 sValue        : string;
 FormatSettings: TFormatSettings;
begin
   FormatSettings.DecimalSeparator :='.';
   FormatSettings.ThousandSeparator:=',';
   //transform the output string to fit with the TParser logic
   OutPut:=StringReplace(OutPut,'+',' ',[rfReplaceAll]); //replace  '+' sign with a space
   OutPut:=StringReplace(OutPut,'-',' -',[rfReplaceAll]); //insert a empty space after of a '-' sign

   StringStream:=TStringStream.Create(OutPut);
   Parser:=TParser.Create(StringStream);
   try
        while Parser.Token <> toEOF do
        begin
           sValue:=Parser.TokenString; //get the string 
           dValue:=StrToFloat(sValue,FormatSettings); //convert the string
           //do something with the float value
           Writeln(FloatToStr(dValue));
           Parser.NextToken;
        end;
   finally
     Parser.Free;
     StringStream.Free;
   end;
end;

begin
  try
    ProcessModBosOutPut('+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003');
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
  Readln;
end.
3 голосов
/ 09 февраля 2011
program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

const
  CString = '+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003';


var
  i,idx: Integer;
  tmpArray: Array[0..7] of Double;
  tmpString: ShortString;

begin
  DecimalSeparator := '.';

  idx := Low(tmpArray);
  tmpString := '';



  tmpString := CString[1];
  for i := 2 to Length(CString) do
  begin
    if CString[i] in ['+', '-']
    then begin
      TryStrToFloat(tmpString, tmpArray[idx]);
      Inc(idx);
      tmpString := CString[i];
    end
    else begin
      tmpString := tmpString + CString[i];
    end;
  end;
  TryStrToFloat(tmpString, tmpArray[idx]);


  for i := Low(tmpArray) to High(tmpArray) do
  begin
    Writeln(FloatToStr(tmpArray[i]));
  end;


  ReadLn;
end.
2 голосов
/ 09 февраля 2011

Для тех, кто заинтересован в повторении тестов производительности, следующее может быть скопировано и вставлено в новый консольный проект с использованием Delphi XE.

program Project1;

uses
  classes,
  sysutils,
  strutils,
  math;

{$APPTYPE CONSOLE}

type
  TFloatArray = array[0..7] of Double;

procedure ParseFloats_TheFox(const aFloatStr: string;
  var aFloatArray: TFloatArray);
var
  lPos: Integer;
  lNextPos: Integer;
  lPosPositive: Integer;
  lPosNegative: Integer;
  i: Integer;
  lFormatSettings: TFormatSettings;
begin
  //do not forget formatsettings, or you will get problems with regional settings
  lFormatSettings.DecimalSeparator := '.';
  lFormatSettings.ThousandSeparator := ',';
  lPos := 1;
  for i := 0 to High(aFloatArray) do
  begin
    lPosPositive := PosEx('+', aFloatStr, lPos + 1);
    lPosNegative := PosEx('-', aFloatStr, lPos + 1);
    if lPosPositive = 0 then
      lNextPos := lPosNegative
    else if lPosNegative = 0 then
      lNextPos := lPosPositive
    else
      lNextPos := Min(lPosPositive, lPosNegative);
    if lNextPos = 0 then
      lNextPos := Length(aFloatStr) + 1;
    //aFloatArray[i] := StrToFloat(Copy(aFloatStr, lPos, lNextPos - lPos), lFormatSettings);
    WriteLn(StrToFloat(Copy(aFloatStr, lPos, lNextPos - lPos), lFormatSettings));
    lPos := lNextPos;
  end;
end;

procedure ProcessModBosOutPut_RRUZ(OutPut : string);
var
 StringStream  : TStringStream;
 Parser        : TParser;
 dValue        : Double;
 sValue        : string;
 FormatSettings: TFormatSettings;
begin
   FormatSettings.DecimalSeparator :='.';
   FormatSettings.ThousandSeparator:=',';
   //transform the output string to fit with the TParser logic
   OutPut:=StringReplace(OutPut,'+',' ',[rfReplaceAll]); //replace  '+' sign with a space
   OutPut:=StringReplace(OutPut,'-',' -',[rfReplaceAll]); //insert a empty space after of a '-' sign

   StringStream:=TStringStream.Create(OutPut);
   Parser:=TParser.Create(StringStream);
   try
        while Parser.Token <> toEOF do
        begin
           sValue:=Parser.TokenString; //get the string
           dValue:=StrToFloat(sValue,FormatSettings); //convert the string
           //do something with the float value
           Writeln(FloatToStr(dValue));
           Parser.NextToken;
        end;
   finally
     Parser.Free;
     StringStream.Free;
   end;
end;


procedure Jorn(const floatstring: string);
var
  i,idx: Integer;
  tmpArray: Array[0..7] of Double;
  tmpString: ShortString;
begin
  DecimalSeparator := '.';

  idx := Low(tmpArray);
  tmpString := '';

  tmpString := floatstring[1];
  for i := 2 to Length(floatstring) do
  begin
    if floatstring[i] in ['+', '-']
    then begin
      writeln(strtofloat(tmpString));
      //TryStrToFloat(tmpString, tmpArray[idx]);
      Inc(idx);
      tmpString := floatstring[i];
    end
    else begin
      tmpString := tmpString + floatstring[i];
    end;
  end;
  //TryStrToFloat(tmpString, tmpArray[idx]);
  writeln(strtofloat(tmpString));
end;

//call like this
var
  lFloats: TFloatArray;
  I: Integer;
begin
  for I := 0 to 999 do
  begin
    ParseFloats_TheFox      ('+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003', lFloats);
    WriteLn('The Fox');

    ProcessModBosOutPut_RRUZ('+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003');
    WriteLn('RRUZ');

    Jorn                    ('+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003');
    WriteLn('Jorn');
  end;

  readln;
end.
...