Something like StrTok() or Sscanf()?
So, I am reading from开发者_开发百科 ModBos over serial port and get readings something like the following : '+0020.8+0022.8-00.046-00.002-00.005-001.99+00.000+00.003';
Basically, there will always be 8 floating point readings, preceded by a plus or minus sign, although they may be of varying character length.
What's the most efficient way to get the values an array of float (or array of string or TSringList)?
I am not certain, but this might be time critical, so efficiency probably has way over elegance.
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;
Because there are always 8 float values, a fixed array of 8 doubles is enough. I kept string manipulation to a minimum, only once per floating point value a string is copied. Important is the TFormatSettings, otherwise you will get errors on systems where the decimal separator is not a dot (like mine).
There is no exception handling here, I expect a string with 8 floating point values, nothing more, nothing less.
You can download and use VC++ sscanf ported to Delphi.
you can use the TParser
class to parse your string.
check this sample application
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.
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.
For those interested in repeating the performance tests, following can be copied and pasted in 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.
精彩评论