2011-02-09 84 views
3

所以,我從通過串行端口ModBos閱讀和獲取的讀數類似如下:'+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003';類似StrTok()或Sscanf()?

基本上,總是會有8個浮點讀數,通過加號或減號,雖然他們可能是不同的字符長度。

什麼是最有效的方式來獲取浮點數組的值(或數組的字符串或TSringList)?

我不確定,但這可能是時間關鍵,所以效率可能超過優雅。

+0

縮略語總是讓我想起2000年的:Sundak,Mondak,Tuesdak,.... ;-) – 2011-02-09 11:56:17

+4

你可能想看看進入[FastCode項目(http://www.google.com/search?sourceid = chrome&q = delphi + fastcode)並查看它們是否具有與您正在查找的內容類似的優化版本。 – 2011-02-09 14:14:38

+0

+謝謝。這是一個有用的鏈接 – Mawg 2011-02-10 02:38:53

回答

8

我會做這樣的事情:

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個浮點值的字符串,沒有什麼比這更少的了。

6

你可以下載並使用VC++ sscanf移植到Delphi。

+2

如果你想調用sscanf我只是調用msvcrt.dll這是一個官方的Windows系統組件。 – 2011-02-09 10:30:50

+1

兩個區別:我的sscanf不依賴於msvcrt.dll,並且%s不與PChar一起使用,但使用String。 – 2011-02-09 11:19:56

+2

由於OP需要性能,我敢打賭,msvcrt.dll中的一個擊敗了在這裏建議的一切! ;-) – 2011-02-09 13:20:49

4

你可以使用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
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

對於那些有興趣在重複的性能測試,下面就可以進行復制和使用德爾福XE一個新的控制檯項目粘貼。喜歡的strtok

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.