sprintf in Delphi?
Asked Answered
W

5

6

Does anyone know a 100% clone of the C/C++ printf for Delphi? Yes, I know the System.Format function, but it handles things a little different.

For example if you want to format 3 to "003" you need "%03d" in C, but "%.3d" in Delphi.

I have an application written in Delphi which has to be able to format numbers using C format strings, so do you know a snippet/library for that?

Thanks in advance!

Westbrook answered 18/3, 2010 at 17:19 Comment(2)
You can't format 3.14 to "003" with "%03d".Luce
Sorry, should've been an integer, fixed ;) Question remains valid :)Westbrook
R
16

You could use the wsprintf() function from Windows.pas. Unfortunately this function is not declared correctly in the Windows.pas so here is a redeclaration:

function wsprintf(Output: PChar; Format: PChar): Integer; cdecl; varargs;
  external user32 name {$IFDEF UNICODE}'wsprintfW'{$ELSE}'wsprintfA'{$ENDIF};

procedure TForm1.FormCreate(Sender: TObject);
var
  S: String;
begin
  SetLength(S, 1024); // wsprintf can work only with max. 1024 characters
  SetLength(S, wsprintf(PChar(S), '%s %03d', 'Hallo', 3));
end;
Rillet answered 18/3, 2010 at 17:42 Comment(3)
Wow ok, I was a little too late answering my own question ;) Thank you, I think this is a much better solution than importing from msvcrt.dll! Will try what one later...Westbrook
After realizing that the user32.wsprintf(W|A) method is unable to handle floating points, I now decided to use msvcrt._vsnw?printf for now using your vararg-fix below. Best way would've been a version without external dependencies, but msvcrt.dll should be available everywhere I need it.Westbrook
importing from msvcrt is a perfectly good solution since it's a system component and almost certainly it's loaded in your process alreadyNomo
R
9

If you want to let the function look more Delphi friendly to the user, you could use the following:

function _FormatC(const Format: string): string; cdecl;
const
  StackSlotSize = SizeOf(Pointer);
var
  Args: va_list;
  Buffer: array[0..1024] of Char;
begin
  // va_start(Args, Format)
  Args := va_list(PAnsiChar(@Format) + ((SizeOf(Format) + StackSlotSize - 1) and not (StackSlotSize - 1)));
  SetString(Result, Buffer, wvsprintf(Buffer, PChar(Format), Args));
end;

const // allows us to use "varargs" in Delphi
  FormatC: function(const Format: string): string; cdecl varargs = _FormatC;


procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(FormatC('%s %03d', 'Hallo', 3));
end;
Rillet answered 18/3, 2010 at 17:57 Comment(3)
By reading the definition of the C/C++-macros va_start, va_list, va_end.Rillet
Wonderful hack, but seem to have problems (tried on D XE2). Buffer got filled correctly but on SetString first 3 chars of Result (and Buffer too!) got corrupted. Looks like wvsprintf corrupts stack or something like that.Niggerhead
Here's quite tricky code that includes asm register magic: #2306462. Unfortunately it means problems on platforms other than Win32...Niggerhead
N
3

It's not recommended to use (ws)printf since they are prone to buffer overflow, it would be better to use the safe variants (eg StringCchPrintF). It is already declared in the Jedi Apilib (JwaStrSafe).

Numismatics answered 19/3, 2010 at 7:24 Comment(1)
As I didn't want to include Jedi just for this one function, I decided to use _vsnw?printf from msvcrt.dll which should be safe, too, as it's second parameter is the buffer size...Westbrook
W
2

Well, I just found this one:

function sprintf(S: PAnsiChar; const Format: PAnsiChar): Integer;
    cdecl; varargs; external 'msvcrt.dll';

It simply uses the original sprintf function from msvcrt.dll which can then be used like that:

procedure TForm1.Button1Click(Sender: TObject);
var s: AnsiString;
begin
  SetLength(s, 99);
  sprintf(PAnsiChar(s), '%d - %d', 1, 2);
  ShowMessage(S);
end;

I don't know if this is the best solution because it needs this external dll and you have to set the string's length manually which makes it prone to buffer overflows, but at least it works... Any better ideas?

Westbrook answered 18/3, 2010 at 17:46 Comment(0)
P
0

more clean approach without unnecessary type casting

function sprintf(CharBuf: PChar; const Format: PAnsiChar): Integer;
cdecl; varargs; external 'msvcrt.dll';

procedure TForm1.Button1Click(Sender: TObject);
var CharBuf: PChar; 
begin
  CharBuf:=StrAlloc (99);
  sprintf(CharBuf, 'two numbers %d - %d', 1, 2);
  ShowMessage(CharBuf);
  StrDispose(CharBuf);
end;

If you happen to cross compile for Windows CE App. use coredll.dll instead of msvcrt.dll

Paquito answered 7/10, 2013 at 7:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.