| Formatting has always
been an issue in traditional Smalltalk. I have always noted large
number of methods just for printRounded, printTruncated,
printSpaces printZeros and so on. But there is a well documented
method for formatting in the C runtime libraries which Smalltalk
MT makes extensive use of.
The C runtime library uses a 'format specification'. The
documentation can be found
here. Basically, you define a string with a % where you want
to place something and after the % you indicate the way you want
it display.
For example if you were printing a table and you wanted 5
characters per col. Try this code in a workspace.
stock := 'YAHOO Class A'.
^String wsprintf: '%.5s' with: stock.
Notice that the result truncates the string to 5 characters.
The format specification uses %.5s which means 5 characters.
You can have multiple items in a specification string. Each
item consists of: %[flags] [width] [.precision] [{h
| l | I64 | L}]type
You will notice that the String class has two methods for
wsprintf. The first wsprintf:with: is the one we have just used.
The second is wsprintf:va_list: which is an example of a method
with a variable number of arguments. To call this one, we use the
_with version. The compiler gathers up all of the arguments and
places them in the va_list.
Let's put out the stock name and the current time.
stock := 'YAHOO Class A'.
time := Time getLocalTime.
^String wsprintf: 'Stock %.5s at: %2d:%2d'
_with: stock _with: time hour _with:
time minute.
Notice here that time hour and time minute are integers. But we
didn't have to send asString (or printString) to them before
printing. This is because the %d format specification expects a
signed decimal integer argument. This is another major reason to
use wsprintf since it can take native argument types.
The area that traditional Smalltalk has the most difficulty is
with printing floating point numbers. Using the format
specification removes all of those difficulties.
Floats in Smalltalk MT are represented as a 64 bit value (same
as double in C). This means that to pass a float to the wsprintf
API we must pass the full 64 bits. There are 2 ways to do this.
Either pass two 32 bits values, or pass a single 64 bit value. To
pass a 64 bit value, we use _with8: (pass 8 bytes i.e. 64 bits).
stock := 'YAHOO Class A'.
time := Time getLocalTime.
bid := 120.56.
^String wsprintf: 'Stock %.5s at: %2d:%2d is %.0f'
_with: stock _with: time hour _with:
time minute
_with8: bid
Notice that the bid is shown as 120. This is because the format
string specifies no precision (i.e. no decimal places %.0f),
therefore bid is output rounded to zero decimal places and we
didn't have to write some complicated rounding code! The
equivalent code using 32 bits values is: stock :=
'YAHOO Class A'.
time := Time getLocalTime.
bid := 120.56.
^String wsprintf: 'Stock %.5s at: %2d:%2d is %.0f'
_with: stock _with: time hour _with:
time minute
_with: (bid _longAtOffset: 0)
_with: (bid _longAtOffset: 4) If
you want a float output as 8 characters (say to fit in a column)
with a zero fill at the front a to 2 decimal places, use %8.2f
it's as simple as that. And you have saved a lot of potentially
allocated objects just to accomplish the same task. To print
debugging strings you can use the same format specification. Just
use something like: Processor outputDebugLine: '%s
at position %d'
_with: string _with: position So think
about using 'format specifications' to display and manipulate
strings and numbers. |