Module std.format.write
This is a submodule of std
.
It provides two functions for writing formatted output: formatValue
and formattedWrite
. The former writes a single
value. The latter writes several values at once, interspersed with
unformatted text.
The following combinations of format characters and types are
available
s | c | d, u, b, o | x, X | e, E, f, F, g, G, a, A | r | compound | |
---|---|---|---|---|---|---|---|
bool | yes | yes | yes | yes | |||
null | yes | ||||||
integer | yes | yes | yes | yes | yes | ||
floating point | yes | yes | yes | ||||
character | yes | yes | yes | yes | yes | ||
string | yes | yes | yes | ||||
array | yes | yes | yes | ||||
associative array | yes | yes | |||||
pointer | yes | yes | |||||
SIMD vectors | yes | yes | yes | ||||
delegates | yes | yes | yes |
Enums can be used with all format characters of the base type.
Structs, Unions, Classes, and Interfaces
Unions, Classes, and InterfacesAggregate types can define various toString
functions. If this
function takes a FormatSpec or a format string as argument, the function decides
which format characters are accepted. If no toString
is defined and
the aggregate is an input range, it is treated like a range, that is 's', 'r' and a compound specifier are accepted. In all other cases
aggregate types only accept 's'.
toString
should have one of the following signatures:
void toString(Writer, Char)(ref Writer w, const ref FormatSpec!Char fmt)
void toString(Writer)(ref Writer w)
string toString();
Where Writer
is an output range which accepts characters (of type
Char
in the first version). The template type does not have
to be called Writer
.
Sometimes it's not possible to use a template, for example when
toString
overrides Object
. In this case, the following
(slower and less flexible) functions can be used:
void toString(void delegate(const(char)[]) sink, const ref FormatSpec!char fmt);
void toString(void delegate(const(char)[]) sink, string fmt);
void toString(void delegate(const(char)[]) sink);
When several of the above toString
versions are available, the
versions with Writer
take precedence over the versions with a
sink
. string toString()
has the lowest priority.
If none of the above mentioned toString
versions are available, the
aggregates will be formatted by other means, in the following
order
If an aggregate is an input range, it is formatted like an input range.
If an aggregate is a builtin type (using alias this
), it is formatted
like the builtin type.
If all else fails, structs are formatted like Type(field1, field2, ...)
,
classes and interfaces are formatted with their fully qualified name
and unions with their base name.
Example
bool
s are formatted as "true"
or "false"
with %s
and like the
byte
s 1 and 0 with all other format characters.
import std .array : appender;
import std .format .spec : singleSpec;
auto w1 = appender!string();
auto spec1 = singleSpec("%s");
formatValue(w1, true, spec1);
writeln(w1 .data); // "true"
auto w2 = appender!string();
auto spec2 = singleSpec("%#x");
formatValue(w2, true, spec2);
writeln(w2 .data); // "0x1"
Example
The null
literal is formatted as "null"
.
import std .array : appender;
import std .format .spec : singleSpec;
auto w = appender!string();
auto spec = singleSpec("%s");
formatValue(w, null, spec);
writeln(w .data); // "null"
Example
Integrals are formatted in (signed) every day notation with %s
and
%d
and as an (unsigned) image of the underlying bit representation
with %b
(binary), %u
(decimal), %o
(octal), and %x
(hexadecimal).
import std .array : appender;
import std .format .spec : singleSpec;
auto w1 = appender!string();
auto spec1 = singleSpec("%d");
formatValue(w1, -1337, spec1);
writeln(w1 .data); // "-1337"
auto w2 = appender!string();
auto spec2 = singleSpec("%x");
formatValue(w2, -1337, spec2);
writeln(w2 .data); // "fffffac7"
Example
Floating-point values are formatted in natural notation with %f
, in
scientific notation with %e
, in short notation with %g
, and in
hexadecimal scientific notation with %a
. If a rounding mode is
available, they are rounded according to this rounding mode, otherwise
they are rounded to the nearest value, ties to even.
import std .array : appender;
import std .format .spec : singleSpec;
auto w1 = appender!string();
auto spec1 = singleSpec("%.3f");
formatValue(w1, 1337.7779, spec1);
writeln(w1 .data); // "1337.778"
auto w2 = appender!string();
auto spec2 = singleSpec("%.3e");
formatValue(w2, 1337.7779, spec2);
writeln(w2 .data); // "1.338e+03"
auto w3 = appender!string();
auto spec3 = singleSpec("%.3g");
formatValue(w3, 1337.7779, spec3);
writeln(w3 .data); // "1.34e+03"
auto w4 = appender!string();
auto spec4 = singleSpec("%.3a");
formatValue(w4, 1337.7779, spec4);
writeln(w4 .data); // "0x1.4e7p+10"
Example
Individual characters (char
, wchar
, or dchar
) are formatted as
Unicode characters with %s
and %c
and as integers (ubyte
,
ushort
, uint
) with all other format characters. With
compound specifiers characters are
treated differently.
import std .array : appender;
import std .format .spec : singleSpec;
auto w1 = appender!string();
auto spec1 = singleSpec("%c");
formatValue(w1, 'ì', spec1);
writeln(w1 .data); // "ì"
auto w2 = appender!string();
auto spec2 = singleSpec("%#x");
formatValue(w2, 'ì', spec2);
writeln(w2 .data); // "0xec"
Example
Strings are formatted as a sequence of characters with %s
.
Non-printable characters are not escaped. With a compound specifier
the string is treated like a range of characters. With compound specifiers strings are treated differently.
import std .array : appender;
import std .format .spec : singleSpec;
auto w1 = appender!string();
auto spec1 = singleSpec("%s");
formatValue(w1, "hello", spec1);
writeln(w1 .data); // "hello"
auto w2 = appender!string();
auto spec2 = singleSpec("%(%#x%|/%)");
formatValue(w2, "hello", spec2);
writeln(w2 .data); // "0x68/0x65/0x6c/0x6c/0x6f"
Example
Static arrays are formatted as dynamic arrays.
import std .array : appender;
import std .format .spec : singleSpec;
auto w = appender!string();
auto spec = singleSpec("%s");
int[2] two = [1, 2];
formatValue(w, two, spec);
writeln(w .data); // "[1, 2]"
Example
Dynamic arrays are formatted as input ranges.
import std .array : appender;
import std .format .spec : singleSpec;
auto w1 = appender!string();
auto spec1 = singleSpec("%s");
auto two = [1, 2];
formatValue(w1, two, spec1);
writeln(w1 .data); // "[1, 2]"
auto w2 = appender!string();
auto spec2 = singleSpec("%(%g%|, %)");
auto consts = [3.1415926, 299792458, 6.67430e-11];
formatValue(w2, consts, spec2);
writeln(w2 .data); // "3.14159, 2.99792e+08, 6.6743e-11"
// void[] is treated like ubyte[]
auto w3 = appender!string();
auto spec3 = singleSpec("%s");
void[] val = cast(void[]) cast(ubyte[])[1, 2, 3];
formatValue(w3, val, spec3);
writeln(w3 .data); // "[1, 2, 3]"
Example
Associative arrays are formatted by using ':'
and ", "
as
separators, enclosed by '['
and ']'
when used with %s
. It's
also possible to use a compound specifier for better control.
Please note, that the order of the elements is not defined, therefore the result of this function might differ.
import std .array : appender;
import std .format .spec : singleSpec;
auto aa = [10:17.5, 20:9.99];
auto w1 = appender!string();
auto spec1 = singleSpec("%s");
formatValue(w1, aa, spec1);
assert(w1 .data == "[10:17.5, 20:9.99]" || w1 .data == "[20:9.99, 10:17.5]");
auto w2 = appender!string();
auto spec2 = singleSpec("%(%x = %.0e%| # %)");
formatValue(w2, aa, spec2);
assert(w2 .data == "a = 2e+01 # 14 = 1e+01" || w2 .data == "14 = 1e+01 # a = 2e+01");
Example
enum
s are formatted as their name when used with %s
and like
their base value else.
import std .array : appender;
import std .format .spec : singleSpec;
enum A { first, second, third }
auto w1 = appender!string();
auto spec1 = singleSpec("%s");
formatValue(w1, A .second, spec1);
writeln(w1 .data); // "second"
auto w2 = appender!string();
auto spec2 = singleSpec("%d");
formatValue(w2, A .second, spec2);
writeln(w2 .data); // "1"
// values of an enum that have no name are formatted with %s using a cast
A a = A .third;
a++;
auto w3 = appender!string();
auto spec3 = singleSpec("%s");
formatValue(w3, a, spec3);
writeln(w3 .data); // "cast(A)3"
Example
structs
, unions
, classes
and interfaces
can be formatted in
several different ways. The following example highlights struct
formatting, however, it applies to other aggregates as well.
import std .array : appender;
import std .format .spec : FormatSpec, singleSpec;
// Using a `toString` with a writer
static struct Point1
{
import std .range .primitives : isOutputRange, put;
int x, y;
void toString(W)(ref W writer, scope const ref FormatSpec!char f)
if (isOutputRange!(W, char))
{
put(writer, "(");
formatValue(writer, x, f);
put(writer, ",");
formatValue(writer, y, f);
put(writer, ")");
}
}
auto w1 = appender!string();
auto spec1 = singleSpec("%s");
auto p1 = Point1(16, 11);
formatValue(w1, p1, spec1);
writeln(w1 .data); // "(16,11)"
// Using a `toString` with a sink
static struct Point2
{
int x, y;
void toString(scope void delegate(scope const(char)[]) @safe sink,
scope const FormatSpec!char fmt) const
{
sink("(");
sink .formatValue(x, fmt);
sink(",");
sink .formatValue(y, fmt);
sink(")");
}
}
auto w2 = appender!string();
auto spec2 = singleSpec("%03d");
auto p2 = Point2(16,11);
formatValue(w2, p2, spec2);
writeln(w2 .data); // "(016,011)"
// Using `string toString()`
static struct Point3
{
int x, y;
string toString()
{
import std .conv : to;
return "(" ~ to!string(x) ~ "," ~ to!string(y) ~ ")";
}
}
auto w3 = appender!string();
auto spec3 = singleSpec("%s"); // has to be %s
auto p3 = Point3(16,11);
formatValue(w3, p3, spec3);
writeln(w3 .data); // "(16,11)"
// without `toString`
static struct Point4
{
int x, y;
}
auto w4 = appender!string();
auto spec4 = singleSpec("%s"); // has to be %s
auto p4 = Point4(16,11);
formatValue(w4, p4, spec3);
writeln(w4 .data); // "Point4(16, 11)"
Example
Pointers are formatted as hexadecimal integers.
import std .array : appender;
import std .format .spec : singleSpec;
auto w1 = appender!string();
auto spec1 = singleSpec("%s");
auto p1 = () @trusted { return cast(void*) 0xFFEECCAA; } ();
formatValue(w1, p1, spec1);
writeln(w1 .data); // "FFEECCAA"
// null pointers are printed as `"null"` when used with `%s` and as hexadecimal integer else
auto w2 = appender!string();
auto spec2 = singleSpec("%s");
auto p2 = () @trusted { return cast(void*) 0x00000000; } ();
formatValue(w2, p2, spec2);
writeln(w2 .data); // "null"
auto w3 = appender!string();
auto spec3 = singleSpec("%x");
formatValue(w3, p2, spec3);
writeln(w3 .data); // "0"
Example
SIMD vectors are formatted as arrays.
import core .simd; // cannot be selective, because float4 might not be defined
import std .array : appender;
import std .format .spec : singleSpec;
auto w = appender!string();
auto spec = singleSpec("%s");
static if (is(float4))
{
version (X86) {}
else
{
float4 f4;
f4 .array[0] = 1;
f4 .array[1] = 2;
f4 .array[2] = 3;
f4 .array[3] = 4;
formatValue(w, f4, spec);
writeln(w .data); // "[1, 2, 3, 4]"
}
}
Functions
Name | Description |
---|---|
formattedWrite(w, fmt, args)
|
Converts its arguments according to a format string and writes the result to an output range. |
formatValue(w, val, f)
|
Formats a value of any type according to a format specifier and writes the result to an output range. |
Authors
Walter Bright, Andrei Alexandrescu, and Kenji Hara