serenity/Documentation/StringFormatting.md
Linus Groh 6e19ab2bbc AK+Everywhere: Rename String to DeprecatedString
We have a new, improved string type coming up in AK (OOM aware, no null
state), and while it's going to use UTF-8, the name UTF8String is a
mouthful - so let's free up the String name by renaming the existing
class.
Making the old one have an annoying name will hopefully also help with
quick adoption :^)
2022-12-06 08:54:33 +01:00

5 KiB

String Formatting

Many places in Serenity allow you to format strings, similar to printf(), for example DeprecatedString::formatted() , StringBuilder::appendff(), or dbgln(). These are checked at compile time to ensure the format string matches the number of parameters. The syntax is largely based on the C++ std::formatter syntax but there are some differences.

For basic usage, any occurrences of {} in the format string are replaced with the other arguments, converted to string form, in order:

DeprecatedString::formatted("Well, {} my {} friends!", "hello", 42) == "Well, hello my 42 friends!";

If you want to include a literal { in the output, use {{:

DeprecatedString::formatted("{{ {}", "hello") == "{ hello";

You can refer to the arguments by index, if you want to repeat one or change the order:

DeprecatedString::formatted("{2}{0}{1}", "a", "b", "c") == "cab";

To control how the arguments are formatted, add colon after the optional index, and then add format specifier characters:

DeprecatedString::formatted("{:.4}", "cool dude") == "cool";
DeprecatedString::formatted("{0:.4}", "cool dude") == "cool";

Format specifiers

In order, the format can contain:

  • Fill character and alignment
  • Sign
  • # Hash
  • 0 Zero
  • Width
  • Precision
  • Type specifier

Each of these is optional. You can include any combination of them, but they must be in this order.

Fill and alignment

This is an optional fill character, followed by an alignment. The fill character can be anything apart from { or }, and is used to fill any space left when the input has fewer characters than the format requests. By default, it is a space. ( )

The alignment characters are:

  • <: Align left.
  • >: Align right.
  • ^: Align centered.

Sign

  • +: Always display a sign before the number.
  • -: Display a sign for negative numbers only.
  • (space): Display a sign for negative numbers, and a leading space for other numbers.

Hash

# causes an "alternate form" to be used.

For integer types, this adds the number-base prefix after the sign:

  • 0b for binary.
  • 0 for octal.
  • 0x for hexadecimal.

Zero

0 pads the number with leading zeros.

Width and Precision

The width defines the minimum number of characters in the output. The precision is a . followed by a precision number, which is used as the precision of floating-point numbers, or a maximum-width for string values.

Both the width and precision can be provided as a replacement field ({}, optionally including an argument index) which allows you to use an integer argument instead of a hard-coded number.

Type specifiers

Type Effect Example output
nothing default format Anything! :^)
b binary 110, 0b000110
B binary uppercase 110, 0B000110
d decimal 42, +0000042
o octal 043
x hexadecimal ff0, 0x00000ff0
X hexadecimal uppercase FF0, 0X00000FF0
c character a
s string well, hello friends!
p pointer 0xdeadc0de
f float 1.234, -inf
a hex float
A hex float uppercase
hex-dump hexadecimal dump fdfdfdfd, 3030 00

Not all type specifiers are compatible with all input types, of course.

Formatting custom types

You can provide a custom AK::Formatter<Foo> class to format Foo values. For the simplest case where you already have a function that produces a string from your type, that would look like this:

template<>
struct AK::Formatter<Web::CSS::Selector> : Formatter<StringView> {
    ErrorOr<void> format(FormatBuilder& builder, Web::CSS::Selector const& selector)
    {
        return Formatter<StringView>::format(builder, selector.serialize());
    }
};

More advanced formatters that make check for format-specifier flags can be written by referring to the fields in StandardFormatter (which most Formatter classes extend).

Detecting if a type can be formatted

The AK::HasFormatter<T> template has a boolean value representing whether T can be formatted.

The FormatIfSupported<T> makes use of this to return either the formatted value of T, or a series of ?s if the type cannot be formatted. For example:

// B has a Formatter defined, but A does not.
DeprecatedString::formatted("{}", FormatIfSupported { A {} }) == "?";
DeprecatedString::formatted("{}", FormatIfSupported { B {} }) == "B";