Something like StrTok () or Sscanf ()?

So, I am reading ModBos through the serial port and reading something like the following: '+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003';

In principle, there will always be 8 floating point readings preceded by a plus or minus sign, although they may have different character lengths.

What is the most efficient way to get the values ​​of a float array (or a string array or TSringList)?

I'm not sure, but it can be time critical, so efficiency probably has a path to elegance.

+3
source share
5 answers

I would do something like this:

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 . , . TFormatSettings, , (, ).

, , 8 , , .

+8

V++ sscanf, Delphi.

+6

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.
+4
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.
+3

For those interested in repeating performance tests, the following can be copied and pasted into a new console project using 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.
+2
source

Source: https://habr.com/ru/post/1790890/


All Articles