Format output

Search
 

References

  1. MSDN
  2. Roxen
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.