Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Leave Comments, Critiques, and Suggestions Here

Using Tango's Text Formatter

Whereas most people coming from a C/C++ background, or even just seasoned D programmers, are used to using the formatting format of printf (and writef with Phobos), Tango has opted to move on. The printf formatstring format is fairly cryptic and non-obvious, and it has it's limitations. The format in Tango is inspired by the one used in Microsoft's .Net, and is more flexible, powerful and attuned to locale formatting for internationally localized programs.

Background on .Net Formatter

The .Net formatting system consists of several levels, to make it possible to plugin different behaviours on all levels of the process. Especially can different types be formatted by a formatter implementing an interface which in Tango has gotten the name IFormatService. The usefulness of this is shown for instance when a type can be formatted differently according to locale settings on the computer.

Usage

The formatter is accessible through several modules in Tango, depending on your needs. The core formatting functionality is found in tango.text.convert.Format and the Formatter struct, whereas both the String class and Stdout let you format. Stdout from tango.io.Stdout is used in the examples here, as it will output the result on the console.

To use Stdout, start with the following import:

import tango.io.Stdout;

The first and most notable difference from the printf notation is how each argument is denoted in the format string.

Stdout.formatln("An {0} is denoted by braces.", "argument");

The above prints the string "An argument is denoted by braces." to the console. Each argument in the format string is placed in braces (if you need to actually print braces, escape them by doubling up: "{{"). There can be 3 components within these braces (called a format item), all optional. The first component (0 in the example above), is the index into the argument list. This is a simple yet major improvement to the power of the format string.

Stdout.formatln("Many {1} can now be {0} around to make {2} easier,\n 
                 and {1} can also be repeated.", "switched", "arguments", "localization");

The string in the last example will be "Many arguments can now be switched around to make localization easier, and arguments can also be repeated." This example shows that you can position your arguments wherever you like in the format string, and as many times as you want.

Format item components

As mentioned above, a format item (or argument of the format string), has 3 components, where the first is an index into the argument list and is mandatory.

The components are as follows:

  • Index component: An index into the argument list of the format function
  • Alignment component: This one says how much space should be used for this item, if less than needed, the alignment component will be ignored, if more it will be padded. Without this, the space needed will be used.
  • Format string component: This component lets you specify how the argument should be formatted, default is 'G' for General.

Formatting using the optional components

In the format items, the alignment component follows a comma, and the format specifier a colon, so it will always start with an index, then a comma if there is an alignment component, then a colon if there is a format specifier, independently optional.

A couple of typical uses of the alignment component follows.

char[] myFName = "Johnny";
Stdout.formatln("First Name = |{0,15}|", myFName);
Stdout.formatln("Last Name = |{0,15}|", "Foo de Bar");

Stdout.formatln("First Name = |{0,-15}|", myFName);
Stdout.formatln("Last Name = |{0,-15}|", "Foo de Bar");

Stdout.formatln("First name = |{0,5}|", myFName);

The text printed in these two examples will be:

First Name = |         Johnny|
Last Name = |     Foo de Bar|
First Name = |Johnny         |
Last Name = |Foo de Bar     |
First name = |Johnny|

As seen here, a positive number will pad in front of the content, whereas a negative alignment value will pad afterwards. In the last output statement, the alignment is ignored as it won't fully show the input. Thus the alignment can be seen as the minimum space used for outputting the format string arguments.

The format string component can be further used to specify how the format string arguments are formatted. As mentioned above, G can be used for quick and dirty and functions much the same way as %s does in a writef format string.

The complete list of specifiers can be found in the reference manual, but below follows some examples that shows some of the uses. Note that some of the specifiers, for instance C for Currency and the different decimal representations, will be locale dependent.

double avogadros = 6.0221415e23;
Stdout.formatln("I have {0:C} in cash.", 100);
Stdout.formatln("Avogadro's number is {0:E}.", avogadros);
Stdout.formatln("Avogadro's number (with alignment) is {0,4:E}.", avogadros);

The output of this is

I have $100.00 in cash.
Invalid format specifier. // There is bug report on this

Summary

This tutorial has shown how the format strings in Tango work. They are inspired by those found in the .Net framework, and can be used for more flexible use cases than the old printf syntax. The format string specifier takes an index into the argument list, making it possible to use arguments several times, and also place them in arbitrary order. How to format the arguments can be further specified by an alignment component and a format specifier.

Additional Notes

  • The formatting system in Tango is designed to work with locale settings (or Cultures) on the system. How to make proper use of this is part of the Locale tutorial?, inclduing how formatting of dates and times can be done.
  • It is possible to create additional format specfiers, or format services, by implementing the IFormatService interface. This is also how the locale formatting works.
  • Microsoft's documentation on what they call Composite Formatting can be found here with pointers further on from there.

Source

Compilable code examples can be found in the distribution in the example directory. The examples are also linked below.