Cześć.
Rzadko o coś pytam, ale te nowsze środowiska z Unicode i przerabianie kodu żeby ANSI było obsłużóne jak należy nie do końca mi wychodzi. Poniższy kod bazuje na przykładzie @Patryk27 z dawnego wątku na konkurs o printf w Pascalu. I kod poniższy działa ok pod starym Delphi 7.
I tutaj pytanie, jak go poprawić by działał w nowszych wersjach Delphi. Docelowo pod WinAPI z użyciem vargsów jako parametrów? Gdy poprawiam wszystkie PChar
na PAnsiChar
, a nawet string
na Ansistring
to i tak działa mi tylko dla %s
jednego lub wielu, a już dla pozostałych typów nie działa.
Prosił bym przykład kodu. Pewnie na nieco starszych Delphi niz XE8 będzie też problem z jego prawidłowym działaniem. Nawet %d
nie zadziała. Debugguje i chyba źle coś zwraca mi funkcja sprintf
. Mogłbym użyć prostego kodu:
function _FormatC(const Format : Ansistring) : Ansistring; cdecl;
const
StackSlotSize = SizeOf(Pointer);
var
Args : VA_List;
Buffer : array[0..1024] of AnsiChar;
begin
Args := VA_List(PAnsiChar(@Format) + ((SizeOf(Format) + StackSlotSize - 1) and not(StackSlotSize - 1)));
SetString(Result, Buffer, wvsprintfA(Buffer, PAnsiChar(Format), Args));
end;
var
FormatC : function(const Format : Ansistring) : Ansistring; cdecl varargs = _FormatC;
Ale wolę ten rozbudowany na bazie Patryka poniżej, bo obsługuje liczby ułamkowe. No chyba, że macie jakieś inne propozycje. Użycie SysUtils
odpada, bo plik wynikowy spuchnie wtedy niemiłosiernie, a dla samego Format
z array of const
jako parametry nie ma sensu go stosować. A "portowanie" go z VCL do WinAPI jest dla mnie za cięzkie. Z góry dziękuję za wszelkie podpowiedzi z kodem.
unit useful_format;
interface
uses
Windows;
function Internal_SimpleFormat(Format : string) : string; cdecl; forward;
var
SimpleFormat : function(Format : string) : string; cdecl varargs = Internal_SimpleFormat;
implementation
function sprintf(S : PAnsiChar; const Format : PAnsiChar) : integer; cdecl;
varargs; external 'msvcrt.dll';
function Internal_SimpleFormat(Format : string) : string; cdecl;
var
PC : PChar;
Tmp : string;
Arg : Pointer;
Pos, Len : UINT;
DoParse : boolean;
begin
Pos := 1;
Result := '';
Arg := Pointer(Uint(@Format) + sizeof(string));
Len := Length(Format);
while (Pos <= Len) do
begin
DoParse := (Pos = 1) and (Format[Pos] = '%');
if not DoParse then
begin
DoParse := Format[Pos - 1] <> '%';
end;
if not DoParse then
begin
Delete(Format, Pos, 1);
Len := Length(Format);
end;
if (DoParse) and ((Copy(Format, Pos, 2) = '%d')
or (Copy(Format, Pos, 4) = '%.1d')
or (Copy(Format, Pos, 4) = '%.2d')
or (Copy(Format, Pos, 4) = '%.3d')
or (Copy(Format, Pos, 4) = '%.4d')
or (Copy(Format, Pos, 4) = '%.5d')
or (Copy(Format, Pos, 4) = '%.6d')
or (Copy(Format, Pos, 4) = '%.7d')
or (Copy(Format, Pos, 4) = '%.8d')) then
begin
GetMem(PC, 20);
if System.Pos('.', Copy(Format, Pos, 2)) <> 2 then
begin
sprintf(PC, PChar(Copy(Format, Pos, 2)), pinteger(Arg)^);
end
else
begin
sprintf(PC, PChar(Copy(Format, Pos, 4)), pinteger(Arg)^);
end;
Result := Result + PC;
FreeMem(PC);
if System.Pos('.', Copy(Format, Pos, 2)) <> 2 then
begin
Inc(Pos, 2);
end
else
begin
Inc(Pos, 4);
end;
Arg := PChar(Arg) + SizeOf(integer);
end
else
begin
if (DoParse) and (Copy(Format, Pos, 2) = '%s') then
begin
Result := Result + PPChar(Arg)^;
Inc(Pos, 2);
Arg := PChar(Arg) + SizeOf(PChar);
end
else
begin
if (DoParse) and ((Copy(Format, Pos, 2) = '%f')
or (Copy(Format, Pos, 4) = '%.1f')
or (Copy(Format, Pos, 4) = '%.2f')
or (Copy(Format, Pos, 4) = '%.3f')
or (Copy(Format, Pos, 4) = '%.4f')
or (Copy(Format, Pos, 4) = '%.5f')
or (Copy(Format, Pos, 4) = '%.6f')
or (Copy(Format, Pos, 4) = '%.7f')
or (Copy(Format, Pos, 4) = '%.8f')
or (Copy(Format, Pos, 4) = '%.9f')) then
begin
GetMem(PC, 30);
if System.Pos('.', Copy(Format, Pos, 2)) <> 2 then
begin
sprintf(PC, PChar(Copy(Format, Pos, 2)), PDouble(Arg)^);
end
else
begin
sprintf(PC, PChar(Copy(Format, Pos, 4)), PDouble(Arg)^);
end;
Tmp := PC;
FreeMem(PC);
if (System.Pos('.', Tmp) > 0) then
begin
while (Tmp[Length(Tmp)] = '0') do
begin
Delete(Tmp, Length(Tmp), 1);
end;
if (Tmp[Length(Tmp)] = '.') then
begin
Delete(Tmp, Length(Tmp), 1);
end;
end;
Result := Result + Tmp;
if System.Pos('.', Copy(Format, Pos, 2)) <> 2 then
begin
Inc(Pos, 2);
end
else
begin
Inc(Pos, 4);
end;
Arg := PChar(Arg) + SizeOf(Double);
end
else
begin
if (DoParse) and
((Copy(Format, Pos, 2) = '%x')
or (Copy(Format, Pos, 2) = '%X')
or (Copy(Format, Pos, 4) = '%.1x')
or (Copy(Format, Pos, 4) = '%.2x')
or (Copy(Format, Pos, 4) = '%.3x')
or (Copy(Format, Pos, 4) = '%.4x')
or (Copy(Format, Pos, 4) = '%.5x')
or (Copy(Format, Pos, 4) = '%.6x')
or (Copy(Format, Pos, 4) = '%.7x')
or (Copy(Format, Pos, 4) = '%.8x')
or (Copy(Format, Pos, 4) = '%.1X')
or (Copy(Format, Pos, 4) = '%.2X')
or (Copy(Format, Pos, 4) = '%.3X')
or (Copy(Format, Pos, 4) = '%.4X')
or (Copy(Format, Pos, 4) = '%.5X')
or (Copy(Format, Pos, 4) = '%.6X')
or (Copy(Format, Pos, 4) = '%.7X')
or (Copy(Format, Pos, 4) = '%.8X')) then
begin
GetMem(PC, 20);
if System.Pos('.', Copy(Format, Pos, 2)) <> 2 then
begin
sprintf(PC, PChar(Copy(Format, Pos, 2)), PDWORD(Arg)^);
end
else
begin
sprintf(PC, PChar(Copy(Format, Pos, 4)), PDWORD(Arg)^);
end;
Result := Result + PC;
FreeMem(PC);
if System.Pos('.', Copy(Format, Pos, 2)) <> 2 then
begin
Inc(Pos, 2);
end
else
begin
Inc(Pos, 4);
end;
Arg := PChar(Arg) + SizeOf(DWORD);
end
else
begin
Result := Result + Format[Pos];
Inc(Pos);
end;
end;
end;
end;
end;
end;
end.