If someone need it here is a small class mimic the Delphi Format:
namespace DelphiFormat;
interface
uses RemObjects.Elements.RTL;
type
Formater = class
private
fValues : array of Object;
fBuffer : String;
fStart : Integer;
fPos : Integer;
fToken : String;
fPart : String;
fPartNum : Integer;
fWidth : Integer;
fPrec : Integer;
fLeft : Boolean;
method raiseError;
method fillRules(const value : not nullable String);
method evalParam(const value : not nullable String) : String;
method skipUntilParamEnd : Boolean;
method skipUntilParam : Boolean;
method Parse : String;
method asUint64(o: Object): UInt64;
method ToSmallestIntAsString(o : Object) : String;
method ToSmallestUIntAsString(o : Object) : String;
protected
constructor(const value : String; const values : array of Object);
public
class method Format(const value : String; const values : array of Object) : String;
end;
implementation
method Formater.raiseError;
begin
raise new FormatException;
end;
method Formater.fillRules(const value: not nullable String);
begin
var lmax := value.Length-1;
var lpos : Integer := 0;
loop begin
// If it is a Number it can be the paramnum or the Width
if Char.IsNumber(value[lpos]) then begin
var lStart := lpos;
while (lpos ≤ lmax) and Char.IsNumber(value[lpos]) do inc(lpos);
if lpos <> lStart then begin
var firstArg := Convert.ToInt32(value.Substring(lStart, (lpos-lStart)));
if (lpos ≤ lmax) and (value[lpos] = ':') then begin
fPartNum := firstArg;
inc(lpos);
end
else
fWidth := firstArg;
end;
end;
if lpos > lmax then break;
// Paramnum?
if value[lpos] = ':' then
begin
fPartNum := 0;
inc(lpos);
end;
if lpos > lmax then break;
// Left?
if value[lpos] = '-' then begin
fLeft := True;
inc(lpos);
end
else
fLeft := False;
if lpos > lmax then break;
if value[lpos] = '*' then begin
fWidth := -2;
inc(lpos);
end
else
if Char.IsNumber(value[lpos]) then begin
var lStart := lpos;
while (lpos ≤ lmax) and Char.IsNumber(value[lpos]) do inc(lpos);
if lpos <> lStart then begin
fWidth := Convert.ToInt32(value.Substring(lStart, lpos-lStart));
end;
end;
if lpos > lmax then break;
if value[lpos] = '.' then begin
inc(lpos);
if lpos > lmax then break;
if value[lpos] = '*' then begin
fPrec := -2;
inc(lpos);
end
else begin
var lStart := lpos;
while (lpos ≤ lmax) and Char.IsNumber(value[lpos]) do inc(lpos);
if lpos <> lStart then
fPrec := Convert.ToInt32(value.Substring(lStart, lpos-lStart));
end;
end;
break;
end;
end;
method Formater.evalParam(const value: not nullable String): String;
begin
if value.Length < 2 then raiseError;
// type of Param
var temp := value[value.Length-1].ToLower;
fWidth := -1;
fPrec := -1;
if value.Length > 2 then
fillRules(value.Substring(1, value.Length-2));
// Now check the Rules for with and prec...
// They will be set to -2 if we need the param
if fWidth = -2 then begin
fWidth := Integer(fValues[fPartNum]);
inc(fPartNum);
end;
if fPrec = -2 then begin
fPrec := Integer(fValues[fPartNum]);
inc(fPartNum);
end;
case temp of
'd' : begin
result := ToSmallestIntAsString(fValues[fPartNum]);//.ToString;
end;
'u' : begin
result := ToSmallestUIntAsString(fValues[fPartNum]);
end;
'x' : begin
result := Convert.ToHexString(asUint64(fValues[fPartNum]));
end;
'p' : begin
result := Convert.ToHexString(asUint64(fValues[fPartNum]), sizeOf(NativeInt) * 2);
end;
'e', 'f','g','n','m' :
begin
var d:= fValues[fPartNum] as Double;
// Convert.ToDouble(fValues[fPartNum]);
if fPrec > 0 then
result := Convert.ToString(d, fPrec, Locale.Current)
else
result := d.ToString;
end;
else result := fValues[fPartNum].ToString;
end; // Case
if (fWidth > 0) and (result.Length < fWidth) then
begin
if fLeft then
result := String.Format("{0,-"+fWidth.ToString+"}",result) else
result := String.Format("{0, "+fWidth.ToString+"}",result);
end;
end;
method Formater.skipUntilParamEnd: boolean;
begin
result := false;
var lPos := fPos;
inc(fPartNum);
while fBuffer[lPos] <> #0 do
begin
case fBuffer[lPos].ToLower of
'a'..'z' : begin
inc(lPos);
fPart := evalParam(fBuffer.Substring(fPos, lPos-fPos));
result := true;
break;
end;
else
inc(lPos);
end;
end;
fPos := lPos;
end;
method Formater.skipUntilParam: Boolean;
begin
fToken := nil;
fPart := nil;
while (fBuffer[fPos] <> #0) do
begin
case fBuffer[fPos] of
'%' : begin
if fBuffer[fPos+1] = '%' then
inc(fPos)
else
begin
fToken := fBuffer.Substring(fStart, fPos-fStart);
if skipUntilParamEnd then
exit true
else
exit false;
end;
end;
else
inc(fPos);
end;
end;
// Maybe there is some Text
if fStart < (fPos) then
begin
fToken := fBuffer.Substring(fStart, (fPos) -fStart);
exit true
end;
exit false;
end;
method Formater.Parse: String;
begin
fStart := 0;
fPos := 0;
fPartNum := -1;
var Builder := new StringBuilder;
while skipUntilParam do
begin
if not String.IsNullOrEmpty(fToken) then
Builder.Append(fToken);
if not String.IsNullOrEmpty(fPart) then
Builder.Append(fPart);
fStart := fPos;
end;
if Builder.Length > 0 then
result := Builder.ToString
else result := nil;
end;
constructor Formater(const value: String; const values: array of Object);
begin
inherited constructor();
fBuffer := value+#0;
fValues := values;
end;
class method Formater.Format(const value: String; const values: array of Object): String;
begin
var Lformater := new Formater(value, values);
result := Lformater.Parse;
end;
method Formater.asUint64(o: Object): UInt64;
begin
if o is SByte then exit SByte(o);
if o is Int16 then exit Int16(o);
if o is Int32 then exit Int32(o);
if o is Int64 then exit Int64(o);
if o is Byte then exit Byte(o);
if o is UInt16 then exit UInt16(o);
if o is UInt32 then exit UInt32(o);
if o is UInt64 then exit UInt64(o);
raise new ArgumentException('Unknown type for o');
end;
method Formater.ToSmallestIntAsString(o: Object): String;
begin
if o is SByte then exit SByte(o).ToString;
if o is Int16 then exit Int16(o).ToString;
if o is Int32 then exit Int32(o).ToString;
if o is Int64 then exit Int64(o).ToString;
if o is Byte then exit Byte(o).ToString;
if o is UInt16 then exit UInt16(o).ToString;
if o is UInt32 then exit UInt32(o).ToString;
if o is UInt64 then exit UInt64(o).ToString;
raise new ArgumentException('Unknown type for o');
end;
method Formater.ToSmallestUIntAsString(o: Object): String;
begin
if o = nil then raise new ArgumentNullException('o is null');
if o is SByte then exit Byte(SByte(o)).ToString;
if o is Int16 then exit UInt16(Int16(o)).ToString;
if o is Int32 then exit UInt32(Int32(o)).ToString;
if o is Int64 then exit UInt64(Int64(o)).ToString;
if o is Byte then exit Byte(o).ToString;
if o is UInt16 then exit UInt16(o).ToString;
if o is UInt32 then exit UInt32(o).ToString;
if o is UInt64 then exit UInt64(o).ToString;
raise new ArgumentException('Unknown type for o');
end;
end.
Test:
class method Main(args: array of String): Int32;
begin
writeLn(Formater.Format('Integer %20d',[100]));
writeLn(Formater.Format('UINT %20U',[-100]));
writeLn(Formater.Format('HEX %20x',[1024]));
writeLn(Formater.Format('HEX %20x',[-1]));
var li : UInt32 := Integer(-1);
writeLn(Formater.Format('UINT %20U',[li]));
var li2 : UInt64 := UInt64(li) *3;
writeLn(Formater.Format('INT64 %20D',[li2]));
writeLn(Formater.Format('UINT64 Neg %20U',[-li2]));
writeLn(Formater.Format('This is a String: %1:s',['First', 'Second']));
writeLn(Formater.Format('This is a String: %:20s und das: %s',['Test','Test2']));
writeLn(Formater.Format('This is a String: %:-20s und das: %s',['Test','Test2']));
writeLn(Formater.Format('String: %s Integer: %d',['Test',1]));
writeLn(Formater.Format('Float: %8.2fT Integer: %d',[9.123456,1]));
writeLn(Formater.Format('Float: %-*.*g Integer: %d',[8,4,993027.123456,1]));
writeLn(Formater.Format('Float: %g T',[0.1234567893245767]));
{$IF NOT ECHOES}
var i := 200;
writeLn(Formater.Format('Pointer: %p',[NativeUInt (@i)]));
{$ENDIF}
end;
end;