'''`[http://www.tcl.tk/man/tcl/TclCmd/format.htm%|%format]`''', a [Tcl Commands%|%built-in] Tcl command, formats a string in the style of sprintf
** Synopsis **
: '''format''' ''formatString'' ?''arg arg ...''?
** Documentation **
[http://www.tcl.tk/man/tcl/TclCmd/format.htm%|%man page]:
** Description **
This command generates a formatted string in a manner similar to the [ANSI] [C] sprintf
procedure (it uses sprintf in its implementation). ''formatString'' indicates how
to format the result, using `%` conversion specifiers as in sprintf, with the additional ''arg'' values, if any, provide values to be substituted into the ''formatString''.
The value of `format` is the formatted string.
Without a size modifier, the numeric conversion types are all subject to
underflow/overflow. The '''`ll`''' size modifier configures a conversion type
to operate on a number of arbitrary size.
** Maximum Width for Numbers **
by default, `format` doesn't provide a way to specify a maxiumum number of digits when using a numeric conversion type such as `%d`. You
could specify the format as a `%s` and then provide a maximum number of
characters, or you could write tcl code to check for maximum. Jonathan Bromley, [comp.lang.tcl] 2007-09, posted the following code (modernized here), which provides a
first cut at a `-strict` initial argument to `format`.
======
proc strictformat {fmt value} {
set f [format $fmt $value]
regexp {%(\d+)} $fmt -> maxwidth
if {[string length $f] > $maxwidth} {
return [string repeat * $maxwidth]
} else {
return $f
}
}
rename format _format
proc format args {
if {[lindex $args 0] eq {-strict}} {
strictformat {*}$args
} else {
_format {*}$args
}
}
======
** Make Unsigned Values **
You can use `format` to produce unsigned integers for display (but don't
reckon with them - for `[expr]` they're still signed!):
======
% format %u -1
4294967295
======
See [floating-point formatting] for discussion on how to write format strings
to handle floats...
[DKF]: Note that 8.5 makes this sort of thing much less necessary as we can now
handle arbitrary width integers.
** Nice-Looking Floats **
To make numbers look nice:
======
set fah [format {%0.2f} [expr {$temperature_cel * 9 / 5 + 32}]]
======
** Color Formatting **
======
set color [format #%02x%02x%02x $r $g $b]
======
** Converting Characters **
A limited formatting of decimals to characters is available in other languages,
e.g. `CHR()` in Basic. If you use that more often, here's a cute shortcut:
======none
interp alias {} chr {} format %c
% set a [chr 49][chr 48]
10
======
** Abbreviating Integers **
See [Narrow formatting] for short rendering of big integers, with powers of
1024:
======none
% fixform 12345678
11.7M
======
** Understanding Formats **
This method should get format string and explain the format structure. This is
a fast scatch:
======
proc explainFormat {formatStr vars} {
set index 1
foreach frm [split $formatStr %] {
set extra {}
set size 0
regexp {([0-9]+)([duioxXcsfegG])(.*)} $frm => size type extra
if {$size == 0} {
set size [string length $frm]
} else {
set frm "%$size$type [lindex $vars 0]"
set vars [lrange $vars 1 end]
set size [string trimleft $size 0]
}
for {set i 0} {$i < 2} {incr i} {
set newIndex [expr {$size +$index -1}]
puts "$index-$newIndex '$frm'"
set index [expr {$newIndex +1}]
if {$extra eq {}} {
break
} else {
set frm $extra
set size [string length $extra]
}
}
}
}
======
======none
% explainFormat hello%02s000%3d $a $b
1-5 'hello'
6-7 '%02s $a'
8-10 '000'
11-13 '%3d $b'
======
** Emulating [Fortran] **
[RS] 2007-09-04: Here's emulating the [Fortran] behavior that numbers too large
for the format are marked as an asterisk string:
======
proc strictformat {fmt value} {
set f [format $fmt $value]
regexp {%(\d+)} $fmt -> maxwidth
if {[string length $f]>$maxwidth} {
return [string repeat * $maxwidth]
} else {return $f}
}
======
Testing:
======none
% strictformat %5.2f 12.345
12.35
% strictformat %5.2f 123.45
*****
% strictformat %5.2f 12345.67
*****
======
** Restricting Floats **
While using Tcl 8.5, you will begin to see strings like 0.0052499999999999995
where before you were seeing values like 0.00525. To round the value to a
shorter value, try something like:
======
format %.3g 0.0052499999999999995
======
See Also [Floating-point formatting]
** Rebasing **
Don Porter, [comp.lang.tcl], in reply to a question about how to go
from base 10 to another base, such as 2 or 16, using arbitrarily large numbers
in Tcl 8.5:
> I would have guessed that `format %x` should do the job, but apparently<
>
> it's currently limited to 64 bits...
======none
% format %llx 1234567890123456789012345
1056e0f36a6443de2df79
======
[DrASK]: Those are ELLs above in `%llx`. Not {percent eleven lower-case-x}, but rather {percent, ell, ell, lower-case-x}.
** Digit Grouping **
Digit grouping can make numbers with many digits easier to read.
[ET]: While I never liked the language [Ada], it did have an idea that I wish had
caught on, the optional use of an underscore character in large numerical
constants, to make the numbers readable. (And trival for a compiler or
interpreter to scan/parse).
So, `1_234_567` is a number that is as readable as `1,234,567` and is much better
than `1234567`. I have a handy little converter, (which I stole from somewhere on
this wiki and modifed):
======
proc commas {var {num 3} {char ,}} {
set len [string length $var]
set first [expr $len - $num]
set x {}
while {$len > 0} {
# grab left num chars
set lef [string range $var $first end]
if {[string length $x] > 0} {
set x "${lef}$char${x}"
} else {
set x $lef
}
# grab everything except left num chars
set var [string range $var 0 [expr $first -1]]
set len [string length $var]
set first [expr {$len - $num}]
}
return $x
}
======
Here are some examples of its use:
======none
dec2bin 987654
11110001001000000110
% commas [dec2bin 987654] 4 _
1111_0001_0010_0000_0110
% commas [dec2bin 987654] 4 { }
1111 0001 0010 0000 0110
% commas [dec2bin 987654] 1 { }
1 1 1 1 0 0 0 1 0 0 1 0 0 0 0 0 0 1 1 0
%commas 123456789 ;# naturally, it defaults for use with large decimal integers
123,456,789
% commas 123456789 3 _ ;# and here's how I wish numbers could be entered, in tcl and in C etc.
123_456_789
% puts "0x[commas [format %08X 123456789] 4 _]" ;# and for hex numbers as well
0x075B_CD15
======
** Misc **
[LV]: The man page for 8.4 is missing examples. 8.5 is better, but I'm looking
for an example of the following. I have a report line that I am trying to fill
out. It consists of a time stamp, a date stamp, and 2 text strings. each of
these items must begin in a specific column.
======
set g OHIO
set fmtg [format %-25.25s $g]
puts [string length $fmtg]
======
The man page is complex enough that I want to be certain that I am not missing
something. This seems to ensure that if g is longer than 25 characters, it is
truncated, and if it is shorter than 25 characters, that it is left justified
and blank padded. Are there any ''[gotchas]'' of which I need to be aware?
''[[TODO: Explain XPG positional format specifiers.]]''
-----
[dbohdan] 2014-06-06: Observation: you could use `format` to do multiple ad hoc type assertions in the vein of `[assert] [[[string is] integer $var]` with a single command. E.g.,
======
eltclsh > set a 5
eltclsh > set b 7
eltclsh > format %d%d $a $b
57
eltclsh > set a NaNNaNNaN
eltclsh > format %d%d $a $b
expected integer but got "NaNNaNNaN"
======
This may or may not be a bad idea.
** See Also **
[Binary representation of numbers]: Operations that, in contrast with `format`, work on integers of arbitrary size.
<> Arts and crafts of Tcl-Tk programming | Command | Binary Data | String Processing