Interesting function, Panofish, thank you, here is my version
;;; - Formatting examples:
; outputdebug DBGVIEWCLEAR
; outputdebug % printf("%-4s %-10s %-10s %-7s %4-s", "ID", "FirstName", "LastName", "Number", "Code")
; outputdebug % printf("%'-'4s %'-'10s %'-'10s %'-'7s %'-'4s", "", "", "", "", "" )
; outputdebug % printf("%'.'-4d %'.'-10s %'.'-10s %'.'7d %'.'-4s", 1, "Alan", "Lilly", 3.999, "A")
; outputdebug % printf("%'.'-4d %'.'-10s %'.'-10s %'.'7.3f %'.'-4s", 2, "Joe", "Apples", 3.999, "B")
; outputdebug % printf("%'.'-4d %'.'-10s %'.'-10s %07.0f %'.'-4s", 3, "Thomas", "Sterling", 3.999, "C")
;;======================================================================
;; ahk printf function
;;
;; Tested with AutoHotkey_L:
;; 1.1.08.01
;;
;; Date:
;; 2012.10.28
;;
;; original "printf()" by panofish, this version is a modification
;;
;; closely emulates perl and c++ printf
;; currently supports float, hex, integer, string, char
;; it supports column size, justification and decimal precision
;;
;; Revision:
;; 2012.11.01
;; Added: Passing in a user defined pad-character for numbers
;; Some minor changes
;;----------------------------------------------------------------------
;;
;; case sensitive delimiter characters for:
;; char ......: "%c" [integers are converted to char, eg. 65 -> "A"]
;; integer ...: "%d","%i" [floats get rounded to the nearest integer,
;; hexadecimals are converted to integer]
;; float .....: "%f" [integers are converted to floats, eg. 1 -> 1.0]
;; float : "%fix" [float integer extraction, eg. 3.6 -> 3]
;; string ....: "%s" [treats numbers as strings]
;; hexadecimal: "%x" [0xFF -> 0xff, integers are converted to hex]
;; hexadecimal: "%X" [0xff -> 0xFF, integers are converted to HEX]
;;
;; known limitations:
;; delimiter chars "%" and "c" to "X" can not be used as pad character
;;
;; char format:
;; printf("%['<PadChar>'][-/0][TotalWidth]c", 65)
;; [-] for left justify
;; [0] to indicate zerofill
;;
;; float format:
;; printf("%['<PadChar>'][-/0][TotalWidth.DecimalPlaces]f", 3.1416)
;; [-] for left justify
;; [0] to indicate zerofill
;;
;; hexadecimal format:
;; printf("%['<PadChar>'][-][TotalWidth]x", 0xff)
;; [-] for left justify
;; literal integers will be written as hexadecimal
;;
;; integer format:
;; printf("%['<PadChar>'][-/0][TotalWidth]d", 255)
;; [-] for left justify
;; [0] to indicate zerofill
;;
;; string format:
;; printf("%['<PadChar>'][-][TotalWidth]s", "ABC")
;; [-] if TotalWidth > StringWidth for left justify
;; [-] if TotalWidth < StringWidth for truncation from string end
;; [] if TotalWidth < StringWidth for truncation from string start
;;
;;----------------------------------------------------------------------
;;
;; PADDING AND TRUNCATION FOR STRING TYPE:
;; pattern:
;; "%[-][TotalWidth][.][-][StringWidth]s"
;;
;; example usage: printf("%20s", "lowlevel")
;;
;; format examples:
;;
;; "%s" "lowlevel"
;; "%3s" "low"
;; "%-5s" "level"
;; "%20s" " lowlevel"
;; "%-20s" "lowlevel "
;; "%20.3s" " low"
;; "%-20.3s" "low "
;; "%20.-5s" " level"
;; "%-20.-5s" "level "
;;
;;
;; PASSING IN A USER DEFINED PAD-CHARACTER FOR STRING TYPE:
;; The default pad character is hard coded as a space,
;; the pad flags are hard coded as a "'", if you want to
;; change it, look for "padCharFlag1" and "padCharFlag2"
;;
;; pattern:
;; "%'<PadChar>'[-][TotalWidth][.][-][StringWidth]s"
;;
;; example usage: printf("%'.'20s", "hello, world")
;;
;; format examples:
;;
;; "%'.'s" "hello, world"
;; "%'.'5s" "hello"
;; "%'.'-5s" "world"
;; "%'.'20s" "........hello, world"
;; "%'.'-20s" "hello, world........"
;; "%'.'20.5s" "...............hello"
;; "%'.'-20.5s" "hello..............."
;; "%'.'20.-5s" "...............world"
;; "%'.'-20.-5s" "world..............."
;;
;; PASSING IN A USER DEFINED PAD-CHARACTER FOR NUMBERS:
;; example usage: printf("%'_'10.4f", 3.141592653589793)
;;
;;======================================================================
;;; uses variadics to handle variable number of inputs
printf(string, prms*)
{
; save original integer and float format
originalIntegerFormat := A_FormatInteger
originalFloatFormat := A_FormatFloat
padCharFlag1 := "'" ; pad character delimiter 1
padCharFlag2 := "'" ; pad character delimiter 2
padCharRegEx := padCharFlag1 "(.*?)" padCharFlag2
for each, prm in prms
{
padChar := " " ; default pad character
RegExMatch(string,"`%(.*?)((fix)|[s|f|c|d|i|x|X])",m)
format := m1
type := m2
; get user defined pad character
userPadCharPos := RegExMatch(format, padCharRegEx, userPadChar)
if (userPadCharPos > 0)
{
if (userPadChar1 <> "")
padChar := substr(userPadChar1, 1, 1)
StringReplace, format, format, %userPadChar%,,
}
;-------------------------------
;;; FLOAT
;-------------------------------
if (type == "f")
{
if format <>
SetFormat, Float, %format%
prm += 0.0
if (userPadCharPos > 0)
StringReplace, prm, prm, %A_Space%, %padChar%, All
}
;-------------------------------
;;; HEXADECIMAL
;-------------------------------
else if (type == "x") or (type == "X")
{
type := (type == "x") ? "h" : "H"
if prm is float
prm := round(prm)
SetFormat, Integer, %type%
prm += 0
if (format < 0)
loop % -format-StrLen(prm)
prm := prm padChar
else if (format > 0)
loop % format-StrLen(prm)
prm := padChar prm
}
;-------------------------------
;;; INTEGER
;-------------------------------
else if (type == "d") or (type == "i") or (type == "fix")
{
if (type == "d") or (type == "i") ; round number
prm := round(prm)
else if (type == "fix") ; float integer extraction
{
StringSplit, part, prm,`.
prm := part1
}
SetFormat, Integer, D
prm += 0 ; bugfix december 10. 2012
if (substr(format,1,1) = "0") ; check zero fill
padChar := "0"
if (format < 0)
loop % -format-StrLen(prm)
prm := prm padChar
else if (format > 0)
loop % format-StrLen(prm)
prm := padChar prm
}
;-------------------------------
;;; STRING
;-------------------------------
else if (type == "s") or (type == "c")
{
if (type == "c") ; integer to char
if prm is integer
prm := Chr(prm)
; split format into total width and string length
IfInString, format,`.
{
StringSplit, part, format,`.
totalWidth := part1
stringWidth := part2
}
else
{
totalWidth := 0
stringWidth := !format ? 0 : format
}
;-------------------------------
; PADDING OR TRUNCATION
;-------------------------------
if !(totalWidth)
{
prmLen := StrLen(prm)
if (abs(stringWidth) > prmLen)
{
if (stringWidth > 0) ; eg. "%20s"
loop % stringWidth-prmLen ; pad on right side
prm := padChar prm
else ; if (stringWidth < 0) ; eg. "%-20s"
loop % -stringWidth-prmLen ; pad on left side
prm := prm padChar
}
else if (abs(stringWidth) < prmLen)
{
if (stringWidth > 0) ; eg. "%5s"
prm := substr(prm, 1, stringWidth) ; cut string from start
else if (stringWidth < 0) ; eg. "%-5s"
prm := substr(prm, stringWidth+1) ; cut string from end
}
}
;-------------------------------
; PADDING AND TRUNCATION
;-------------------------------
else ; if (totalWidth)
{
if (totalWidth > 0)
{
if (stringWidth > 0) ; eg. "%20.5s"
{
prm := substr(prm, 1, stringWidth) ; cut string from start
loop % totalWidth - StrLen(prm) ; pad on left side
prm := padChar prm
}
else if (stringWidth < 0) ; eg. "%20.-5s"
{
prm := substr(prm, stringWidth+1) ; cut string from end
loop % totalWidth - StrLen(prm) ; pad on left side
prm := padChar prm
}
}
else if (totalWidth < 0)
{
if (stringWidth > 0) ; eg. "%-20.5s"
{
prm := substr(prm, 1, stringWidth) ; cut string from start
loop % -totalWidth - StrLen(prm) ; pad on right side
prm := prm padChar
}
else if (stringWidth < 0) ; eg. "%-20.-5s"
{
prm := substr(prm, stringWidth+1) ; cut string from end
loop % -totalWidth - StrLen(prm) ; pad on right side
prm := prm padChar
}
}
}
}
;-------------------------------
;;; UNKNOWN TYPE
;-------------------------------
else
{
msg =
(ltrim join`n comment
Error in RegExMatch(string,...)`n
string%A_Tab% = %string%
format%A_Tab% = %format%
type%A_Tab% = %type%`n
Unknown type = %type% specified in call to printf
)
msgbox, 4096,, %msg%
}
; replace regexmatch 'm' with formatted input value 'prm'
; "%" symbol must be escaped with backtick
StringReplace, string, string, % "`" m, %prm%
}
; restore original integer and float format
SetFormat, Integer, %originalIntegerFormat%
SetFormat, Float, %originalFloatFormat%
return string
}
;; end_of_code
Here is a starting point for a
printf-like function that works in AHK Basic:
; printf_basic - printf-like function for AHK Basic
;
; Formatting example:
;
; _ := A_Tab ; delimiter
; d := 1 ; decimal
; f := 3.1416 ; float
; s := "string" ; string
;
; outputdebug DBGVIEWCLEAR
;
; outputdebug % printf_basic("|%09d|%9.2f|%'.'-9s|" _ d _ f _ s)
;;; uses a string with 'A_Tab' as delimiter to handle
;;; format string and variable number of inputs
printf_basic(string_prms)
{
; save original integer and float format
originalIntegerFormat := A_FormatInteger
originalFloatFormat := A_FormatFloat
padCharFlag1 := "'" ; pad character delimiter 1
padCharFlag2 := "'" ; pad character delimiter 2
padCharRegEx := padCharFlag1 "(.*?)" padCharFlag2
StringGetPos, pos, string_prms, %A_Tab%
string := substr(string_prms, 1, pos)
prms := substr(string_prms, pos+2)
Loop, parse, prms, %A_Tab%
{
prm := A_LoopField
padChar := " " ; default pad character
RegExMatch(string,"`%(.*?)((fix)|[s|f|c|d|i|x|X])",m)
format := m1
type := m2
; get user defined pad character
userPadCharPos := RegExMatch(format, padCharRegEx, userPadChar)
if (userPadCharPos > 0)
{
if (userPadChar1 <> "")
padChar := substr(userPadChar1, 1, 1)
StringReplace, format, format, %userPadChar%,,
}
;-------------------------------
;;; FLOAT
;-------------------------------
; add here the rest code of 'printf' function from above
; ...
; ...
bugfix 10. December 2012