Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

std.format.write

This is a submodule of std.format.
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

Aggregate 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.toString. 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.
Authors:
Examples:
bools are formatted as "true" or "false" with %s and like the bytes 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"
Examples:
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"
Examples:
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"
Examples:
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"
Examples:
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"
Examples:
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"
Examples:
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]"
Examples:
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]"
Examples:
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");
Examples:
enums 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"
Examples:
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)"
Examples:
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"
Examples:
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]"
    }
}
uint formattedWrite(Writer, Char, Args...)(auto ref Writer w, scope const Char[] fmt, Args args);

uint formattedWrite(alias fmt, Writer, Args...)(auto ref Writer w, Args args)
if (isSomeString!(typeof(fmt)));
Converts its arguments according to a format string and writes the result to an output range.
The second version of formattedWrite takes the format string as a template argument. In this case, it is checked for consistency at compile-time.
Parameters:
Writer w an output range, where the formatted result is written to
Char[] fmt a format string
Args args a variadic list of arguments to be formatted
Writer the type of the writer w
Char character type of fmt
Args a variadic list of types of the arguments
Returns:
The index of the last argument that was formatted. If no positional arguments are used, this is the number of arguments that where formatted.
Throws:
A FormatException if formatting did not succeed.

Note In theory this function should be @nogc. But with the current implementation there are some cases where allocations occur. See sformat for more details.

Examples:
import std.array : appender;

auto writer1 = appender!string();
formattedWrite(writer1, "%s is the ultimate %s.", 42, "answer");
writeln(writer1[]); // "42 is the ultimate answer."

auto writer2 = appender!string();
formattedWrite(writer2, "Increase: %7.2f %%", 17.4285);
writeln(writer2[]); // "Increase:   17.43 %"
Examples:
The format string can be checked at compile-time:
import std.array : appender;

auto writer = appender!string();
writer.formattedWrite!"%d is the ultimate %s."(42, "answer");
writeln(writer[]); // "42 is the ultimate answer."

// This line doesn't compile, because 3.14 cannot be formatted with %d:
// writer.formattedWrite!"%d is the ultimate %s."(3.14, "answer");
void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T val, ref scope const FormatSpec!Char f);
Formats a value of any type according to a format specifier and writes the result to an output range.
More details about how types are formatted, and how the format specifier influences the outcome, can be found in the definition of a format string.
Parameters:
Writer w an output range where the formatted value is written to
T val the value to write
FormatSpec!Char f a FormatSpec defining the format specifier
Writer the type of the output range w
T the type of value val
Char the character type used for f
Throws:
A FormatException if formatting did not succeed.

Note In theory this function should be @nogc. But with the current implementation there are some cases where allocations occur. See sformat for more details.

See Also:
formattedWrite which formats several values at once.
Examples:
import std.array : appender;
import std.format.spec : singleSpec;

auto writer = appender!string();
auto spec = singleSpec("%08b");
writer.formatValue(42, spec);
writeln(writer.data); // "00101010"

spec = singleSpec("%2s");
writer.formatValue('=', spec);
writeln(writer.data); // "00101010 ="

spec = singleSpec("%+14.6e");
writer.formatValue(42.0, spec);
writeln(writer.data); // "00101010 = +4.200000e+01"