| @@ -0,0 +1,250 @@ | |||||
| unit uutlSScanf; | |||||
| {$mode objfpc}{$H+} | |||||
| interface | |||||
| uses | |||||
| Classes, SysUtils; | |||||
| function utlSScanf(const s: string; const fmt: string; const Pointers: array of Pointer): Integer; | |||||
| function utlSScanf(const s: string; const fmt: string; const Pointers: array of Pointer; const FormatSettings: TFormatSettings): Integer; | |||||
| implementation | |||||
| function utlSScanf(const s: string; const fmt: string; const Pointers: array of Pointer): Integer; | |||||
| begin | |||||
| Result:= utlSScanf(s, fmt, Pointers, DefaultFormatSettings); | |||||
| end; | |||||
| { | |||||
| 0 on success (perfect match) | |||||
| +N if fmt was consumed without error, but there are characters left in s. First unparsed character is N | |||||
| -N if there was an error consuming pattern character N | |||||
| %s: string, must be terminated by a fixed string in fmt | |||||
| %d: integer | |||||
| %D: int64 | |||||
| %f: double, supports scientific notation | |||||
| %P: hex PtrUInt (fixed length) | |||||
| %x: hex integer | |||||
| %X: hex int64 | |||||
| } | |||||
| function utlSScanf(const s: string; const fmt: string; const Pointers: array of Pointer; const FormatSettings: TFormatSettings): Integer; | |||||
| var | |||||
| pt, fpos, tokfpos, spos: integer; | |||||
| token, limit: string; | |||||
| limitChar: Char; | |||||
| success: Boolean; | |||||
| function nextToken(advance: boolean): string; | |||||
| var | |||||
| oldfpos: integer; | |||||
| begin | |||||
| Result:= ''; | |||||
| oldfpos:= fpos; | |||||
| try | |||||
| if fpos > Length(fmt) then | |||||
| Exit; | |||||
| Result:= fmt[fpos]; | |||||
| inc(fpos); | |||||
| if Result = '%' then begin | |||||
| if fpos > Length(fmt) then | |||||
| Exit; // incomplete token, treat as literal % | |||||
| Result += fmt[fpos]; | |||||
| inc(fpos); | |||||
| if Result = '%%' then | |||||
| Result:= '%'; | |||||
| end; | |||||
| finally | |||||
| if not advance then | |||||
| fpos:= oldfpos | |||||
| else | |||||
| tokfpos:= oldfpos; | |||||
| end; | |||||
| end; | |||||
| function GetString: boolean; | |||||
| var | |||||
| tmp: string; | |||||
| begin | |||||
| tmp:= ''; | |||||
| Result:= false; | |||||
| if limitChar = #0 then begin | |||||
| tmp:= Copy(s, spos, MaxInt); | |||||
| inc(spos, length(tmp)); | |||||
| Result:= true; | |||||
| end else begin | |||||
| while spos <= Length(s) do begin | |||||
| if s[spos] = limitChar then begin | |||||
| dec(spos); // consume again | |||||
| Result:= true; | |||||
| break; | |||||
| end; | |||||
| tmp:= tmp + s[spos]; | |||||
| inc(spos); | |||||
| end; | |||||
| end; | |||||
| PString(Pointers[pt])^:= tmp; | |||||
| end; | |||||
| function GetInteger(i64: boolean): Boolean; | |||||
| var | |||||
| tmp: string; | |||||
| v: int64; | |||||
| begin | |||||
| tmp:= ''; | |||||
| Result:= false; | |||||
| while spos <= Length(s) do begin | |||||
| if s[spos] = limitChar then begin | |||||
| dec(spos); // consume again | |||||
| break; | |||||
| end; | |||||
| tmp:= tmp + s[spos]; | |||||
| if not TryStrToInt64(tmp, v) then begin | |||||
| dec(spos); | |||||
| SetLength(tmp, Length(tmp) - 1); | |||||
| Break; | |||||
| end; | |||||
| inc(spos); | |||||
| end; | |||||
| if TryStrToInt64(tmp, v) then begin | |||||
| if i64 then | |||||
| PInt64(Pointers[pt])^:= v | |||||
| else | |||||
| PInteger(Pointers[pt])^:= v; | |||||
| Result:= true; | |||||
| end; | |||||
| end; | |||||
| function GetFloat: Boolean; | |||||
| var | |||||
| tmp: string; | |||||
| v: double; | |||||
| lastChar: char; | |||||
| firstFailure, fflen: Integer; | |||||
| begin | |||||
| tmp:= ''; | |||||
| Result:= false; | |||||
| firstFailure:= 0; | |||||
| while spos <= Length(s) do begin | |||||
| if s[spos] = limitChar then begin | |||||
| dec(spos); // consume again | |||||
| break; | |||||
| end; | |||||
| tmp:= tmp + s[spos]; | |||||
| if not TryStrToFloat(tmp, v, FormatSettings) then begin | |||||
| lastChar:= tmp[length(tmp)]; | |||||
| if (firstFailure = 0) and | |||||
| ((lastChar = FormatSettings.ThousandSeparator) or (lastChar in ['-','E'])) then begin | |||||
| firstFailure:= spos; | |||||
| fflen:= Length(tmp); | |||||
| end else begin | |||||
| Break; | |||||
| end; | |||||
| end else | |||||
| firstFailure:= 0; | |||||
| inc(spos); | |||||
| end; | |||||
| if firstFailure > 0 then begin | |||||
| spos:= firstFailure; | |||||
| dec(spos); | |||||
| SetLength(tmp, fflen - 1); | |||||
| end; | |||||
| if TryStrToFloat(tmp, v, FormatSettings) then begin | |||||
| PDouble(Pointers[pt])^:= v; | |||||
| Result:= true; | |||||
| end; | |||||
| end; | |||||
| function GetHex(mode: byte): Boolean; | |||||
| const | |||||
| ptrlen = Sizeof(PtrUInt) * 2; | |||||
| var | |||||
| tmp: string; | |||||
| v: int64; | |||||
| begin | |||||
| tmp:= '$'; | |||||
| Result:= false; | |||||
| if mode = 0 then begin | |||||
| tmp += Copy(s, spos, ptrlen); | |||||
| inc(spos, length(tmp)); | |||||
| if Length(tmp) <> ptrlen+1 then | |||||
| Exit; | |||||
| end else | |||||
| while spos <= Length(s) do begin | |||||
| if s[spos] = limitChar then begin | |||||
| dec(spos); // consume again | |||||
| break; | |||||
| end; | |||||
| tmp:= tmp + s[spos]; | |||||
| if not TryStrToInt64(tmp, v) then begin | |||||
| dec(spos); | |||||
| SetLength(tmp, Length(tmp) - 1); | |||||
| Break; | |||||
| end; | |||||
| inc(spos); | |||||
| end; | |||||
| if TryStrToInt64(tmp, v) then begin | |||||
| case mode of | |||||
| 0: PPtrUInt(Pointers[pt])^:= v; | |||||
| 1: PInteger(Pointers[pt])^:= v; | |||||
| 2: PInt64(Pointers[pt])^:= v; | |||||
| end; | |||||
| Result:= true; | |||||
| end; | |||||
| end; | |||||
| begin | |||||
| pt:= 0; | |||||
| fpos:= 1; | |||||
| spos:= 1; | |||||
| while fpos <= length(fmt) do begin | |||||
| if spos > Length(s) then | |||||
| Exit(-fpos); | |||||
| token:= nextToken(true); | |||||
| if token = '' then | |||||
| Exit(-tokfpos); | |||||
| if Length(token) = 1 then begin | |||||
| if token <> s[spos] then | |||||
| Exit(-tokfpos); | |||||
| end else begin | |||||
| limit:= nextToken(false); | |||||
| if Length(limit) = 1 then | |||||
| limitChar:= limit[1] | |||||
| else | |||||
| limitChar:= #0; | |||||
| case token[2] of | |||||
| 's': success:= GetString; | |||||
| 'd': success:= GetInteger(false); | |||||
| 'D': success:= GetInteger(True); | |||||
| 'f': success:= GetFloat; | |||||
| 'P': success:= GetHex(0); | |||||
| 'x': success:= GetHex(1); | |||||
| 'X': success:= GetHex(2); | |||||
| else | |||||
| Exit(-tokfpos); | |||||
| end; | |||||
| if not success then | |||||
| Exit(-tokfpos); | |||||
| inc(pt); | |||||
| end; | |||||
| inc(spos); | |||||
| end; | |||||
| if spos <= Length(s) then | |||||
| Exit(spos) | |||||
| else | |||||
| Exit(0); | |||||
| end; | |||||
| end. | |||||