Formatted Output
This chapter is about features of Phobos's std.format module, not about the core features of the D language.
D's input and output format specifiers are similar to the ones in the C language.
Before going further, I would like to summarize the format specifiers and flags as a reference:
Flags (can be used together)
- flush left
+ print sign
# print in the alternative way
0 print zero-filled
space print space-filled
Format Specifiers
s default
b binary
d decimal
o octal
x,X hexadecimal
e,E scientific floating point
f,F dotted floating point
g,G as e or f
a,A hexadecimal floating point
We have been using functions like writeln with multiple parameters as necessary. The parameters would be converted to their string representations and then
sent to the output.
Sometimes this is not sufficient. The output may have to be in a certain format. Let's look at the following code that is used to print items of an invoice:
items ~= 1.23;
items ~= 45.6;
for (int i = 0; i != items.length; ++i) {
writeln("Item ", i + 1, ": ", items[i]);
}
The output:
Item 1: 1.23 Item 2: 45.6
Despite the information being correct, it may be required to be printed in a certain format. For example the dots may have to be lined up and that there must always be two digits after the dot as in the following output:
Item 1: 1.23 Item 2: 45.60
Formatted output is useful in such cases. The output functions that we have been using so far has counterparts that contain the letter f in their names: writef() and writefln(). The letter f is short for formatted. The first parameter of these functions is a format string that describes how the other parameters should be printed.
For example, the format string that would have writefln() produce the desired output above is the following:
writefln("Item %d:%9.02f", i + 1, items[i]);
The format string contains regular characters that are passed to the output as-is, as well as special format specifiers that correspond to each parameter that is to be printed. Format specifiers start with the % character and ends with a format character. The format string above has two format specifiers: %d and %9.02f.
Every specifier is associated to the following parameters in order. For example, %d is associated with i + 1 and %9.02f is associated with items[i]. Every specifier specifies the format of the parameter that it corresponds to. (Format specifiers may have parameter numbers as well. This will be explained later in the chapter.)
All of the other characters of the format string that are not parts of format specifiers are printed as-is. Such regular characters of the format specifier above are highlighted as red as in "Item %d:%9.02f".
Format specifiers consist of six parts most of which are optional. The part named position will be explained later below. The other five are the following (Note: The spaces between these parts are inserted here to help with readability; they are not parts of the specifiers.):
% flags width precision format_character
The % character at the beginning and the format character at the end are required; the others are optional.
Because % has a special meaning in format strings, when a % needs to be printed as a regular character, it must be typed as %%.
format_character
b: Integer parameter is printed in the binary system.
o: Integer parameter is printed in the octal system.
x and X: Integer parameter is printed in the hexadecimal system; with lowercase letters for x and uppercase letters for X.
d: Integer parameter is printed in decimal system; a negative sign is also printed if it is a signed type and the value is less than zero.
writefln("Binary : %b", value); writefln("Octal : %o", value); writefln("Hexadecimal: %x", value); writefln("Decimal : %d", value);
Binary : 1100 Octal : 14 Hexadecimal: c Decimal : 12
e: Floating point parameter is printed according to the following rules.
- single digit before the dot
- dot if precision is non-zero
- digits after the dot, number of which is determined by precision (default precision is 6)
- the
echaracter (meaning "10 to the power of") - the
-or+character depending on whether the exponent is less than or greater than zero - the exponent consisting of at least two digits
E: Same as in e with the exception of the E character at the output instead of e.
f and F: Floating point parameter is printed in the decimal system; there is at least one digit before the dot and the default precision is 6.
g: Same as in f if the exponent is between -5 and precision; otherwise as in e. precision does not specify the number of digits after the dot, but the significant digits of the entire value. If there are no significant digits after the dot, then the dot is not printed. The rightmost zeros after the dot are not printed.
G: Same as in g with the exception of E or F at the output.
a: Floating point parameter is printed in the hexadecimal floating format:
- the characters
0x - single hexadecimal digit
- dot if precision is non-zero
- digits after the dot, number of which is determined by precision; if no precision is specified, then as many digits as necessary
- the
pcharacter (meaning "2 to the power of") - the
-or+character depending on whether the exponent is less than or greater than zero - the exponent consisting of at least one digit (the exponent of the value 0 is 0)
A: Same as in a with the exception of the 0X and P characters at the output.
double value = 123.456789; writefln("with e: %e", value); writefln("with f: %f", value); writefln("with g: %g", value); writefln("with a: %a", value);
with e: 1.234568e+02 with f: 123.456789 with g: 123.457 with a: 0x1.edd3c07ee0b0bp+6
s: The value is printed in the same way as in non-formatted output, according to the type of the parameter:
boolvalues astrueorfalse- integer values same as
%d - floating point values same as
%g - strings in UTF-8 encoding; precision determines the maximum number of bytes to use (remember that in UTF-8 encoding, the number of bytes is not the same as the number of characters; for example the string "ağ" has 2 characters, consisting a total of 3 bytes)
- struct and class objects as the return value of the
toString()member functions of their types; precision determines the maximum number of bytes to use - arrays as their element values side by side
bool b = true; int i = 365; double d = 9.87; string s = "formatted"; auto o = File("test_file", "r"); int[] a = [ 2, 4, 6, 8 ]; writefln("bool : %s", b); writefln("int : %s", i); writefln("double: %s", d); writefln("string: %s", s); writefln("object: %s", o); writefln("array : %s", a);
bool : true int : 365 double: 9.87 string: formatted object: File(55738FA0) array : [2, 4, 6, 8]
width
This part determines the width of the field that the parameter is printed in. If the width is specified as the character *, then the actual width value is read from the next parameter. If width is a negative value, then the - flag is assumed.
int value = 100; writefln("In a field of 10 characters:%10s", value); writefln("In a field of 5 characters :%5s", value);
In a field of 10 characters: 100 In a field of 5 characters : 100
precision
Precision is specified after a dot in the format specifier. For floating point types, it determines the precision of the printed representation of the values. If the precision is specified as the character *, then the actual precision is read from the next parameter (that parameter must be an int). Negative precision values are ignored.
double value = 1234.56789; writefln("%.8g", value); writefln("%.3g", value); writefln("%.8f", value); writefln("%.3f", value);
1234.5679 1.23e+03 1234.56789000 1234.568
auto number = 0.123456789; writefln("Number: %.*g", 4, number);
Sayı: 0.1235
flags
More than one flag can be specified.
-: the value is printed left-aligned in its field; this flag cancels the 0 flag
int value = 123; writefln("Normally right-aligned:|%10d|", value); writefln("Left-aligned :|%-10d|", value);
Normally right-aligned:| 123| Left-aligned :|123 |
+: if the value is positive, it is prepended with the + character; this flag cancels the space flag
writefln("No effect for negative values : %+d", -50); writefln("Positive value with the + flag : %+d", 50); writefln("Positive value without the + flag: %d", 50);
No effect for negative values : -50 Positive value with the + flag : +50 Positive value without the + flag: 50
#: prints the value in an alternate form depending on the format_character
o: the first character of the octal value is always printed as 0xandX: if the value is not zero, it is prepended with0xor0X- floating points: a dot is printed even if there are no significant digits after the dot
gandG: even the insignificant zero digits after the dot are printed
writefln("Octal starts with 0 : %#o", 1000); writefln("Hexadecimal starts with 0x : %#x", 1000); writefln("Contains dot even when unnecessary: %#g", 1f); writefln("Rightmost zeros are printed : %#g", 1.2);
Octal starts with 0 : 01750 Hexadecimal starts with 0x : 0x3e8 Contains dot even when unnecessary: 1.00000 Rightmost zeros are printed : 1.20000
0: the field is padded with zeros (unless the value is nan or infinity); if precision is also specified, this flag is ignored
writefln("In a field of 8 characters: %08d", 42);
In a field of 8 characters: 00000042
space character: if the value is positive, a space character is prepended to align the negative and positive values
writefln("No effect for negative values: % d", -34); writefln("Positive value with space : % d", 56); writefln("Positive value without space : %d", 56);
No effect for negative values: -34 Positive value with space : 56 Positive value without space : 56
Positional parameters
We have seen above that the parameters are associated one by one with the specifiers in the format string. It is possible to use position numbers within format specifiers. This enables associating the specifiers with specific parameters. Parameters are numbered in increasing fashion, starting with 1. The parameter numbers are specified immediately after the % character, followed by a $:
% position$ flags width precision format_character
An advantage of positional parameters is being able to use the same parameter in more than one places in the same format string:
writefln("%1$d %1$x %1$o %1$b", 42);
The format string above uses the parameter numbered 1 within four specifiers to print it in decimal, hexadecimal, octal, and binary formats:
42 2a 52 101010
Another application of positional parameters is supporting multiple human languages. When referred by position numbers, parameters can be moved anywhere within the specific format string for a given human language. For example, the number of students of a given classroom can be printed as in the following:
writefln("There are %s students in room %s.", count, room);
There are 20 students in room 1A.
Let's assume that the program must also support Turkish. In this case the format string needs be selected according to the active language. The following method takes advantage of the ternary operator:
auto format = (language == "en" ? "There are %s students in room %s." : "%s sınıfında %s öğrenci var."); writefln(format, count, room);
Unfortunately, when the parameters are associated one by one, the classroom and student count information appear in reverse order in the Turkish message; the room information is where the count should be and the count is where the room should be:
20 sınıfında 1A öğrenci var. ← Wrong: means "room 20", and "1A students"!
To avoid this, the parameters can be specified by numbers as 1$ and 2$ to associate each specifier with the exact parameter:
auto format = (language == "en" ? "There are %1$s students in room %2$s." : "%2$s sınıfında %1$s öğrenci var."); writefln(format, count, room);
Now the parameters appear in the proper order, regardless of the language selected:
There are 20 students in room 1A.
1A sınıfında 20 öğrenci var.
format
Formatted output is available by the format() function of the std.string module as well. format() works the same as writef() but it returns the result as a string instead of printing it to the output:
import std.stdio; import std.string; void main() { write("What is your name? "); auto name = chomp(readln()); auto result = format("Hello %s!", name); }
The program can make use of that result in later expressions.
Exercises
- Write a program that reads a value and prints it in the hexadecimal system.
- Write a program that reads a floating point value and prints it as percentage value with two digits after the dot. For example, if the value is 1.2345, it should print
%1.23.
Kitaplar
Forum
Tanıtım
İletişim
Hakları