Skip to content

String formatting

IS4 edited this page Aug 20, 2024 · 12 revisions

Dynamic strings offer enhanced formatting capabilities through functions such as str_format or str_val, modelled after format but with additional syntax and more specifiers and options.

str_format and similar expect a format string, formed from regular characters, copied directly to output, and placeholders, replaced by formatted arguments. str_val corresponds to a single format item without any additional syntax, where str_val(x, .format="f") is roughly equivalent to str_format("{0:f}", x) (except that the tag of x is known).

Brace syntax

A new way of specifying format items is to use the { and } characters. The text between these braces specifies the string that shall replace the placeholder in the output. The initial { should be followed by an unsigned integer specifying the position of the argument (0-based). Next comes a colon (:), followed by the format string up to the first } character. The last character in the format string is a format selector which is used to select the proper type of the value and the allowed formatting options.

Braces that are not a part of a format item can be escaped with %{ and %}. Braces in a format item cannot be escaped in any way. An exception to this syntax is a valid color code (like {FF8000}), which is taken literally.

Braces can also wrap an expression. When encountered, this expression is parsed and executed, like with expr_parse. If the expression is valid, it may also be followed by :, in which case the result is formatted akin to the V selector.

Braces can also be used to specify the locale using a special syntax. All other occurrences of braces that don't match the rules specified above will raise an error.

Example:

str_format("{FFFFFF} %{{0:d}%} %{{1:f}%} %{{2:x}%}", 152, 8.63, 0xABCD); //{FFFFFF} {152} {8.63} {ABCD}

Percent syntax

To support backwards compatibility with most older format strings, the % character can be used as well to denote a format item, but with some syntax extensions. The % character shall be followed by any number of non-letter characters, and the first letter that follows is the format selector, ending the placeholder.

The initial % character may also be followed by a non-negative integer and a dollar sign ($) to indicate a positional argument, similarly to the brace syntax. A sequence of %% outside a format item denotes a literal % character.

Integer extensions

All places where an integer is expected can also contain these special values:

Pattern Meaning Example
^ Incremented positional argument index. {^:d} {^:f} {^:s} is equivalent to %d %f %s.
@index Positional argument at integer index. %0@1d formats a number with the number of leading zeroes in argument at index 1.
-number The opposite value of number.

Format selectors

A format selector is specified last in a format item, denoted by a letter and optionally preceded by options affecting the format.

Letter Format Meaning Example
i [Cwidth][.precision] A signed integer in the decimal base, optionally with a specific width padded with C. If precision is given, the input is treated as fixed-width, with lower precision digits put after the decimal point and interpreted as if with f. 8d pads the integer to the total width of 8 characters with spaces. .2d treats 1234 as 12.34.
u [Cwidth][.precision] An unsigned integer in the decimal base, the rest is like for i.
h or x [Cwidth] An unsigned integer in the hexadecimal base, the rest is like for i. If forced to use on a float (such as with str_val or the V selector), formats it as a hex-float.
o [Cwidth] An unsigned integer in the octal base, the rest is like for i.
f [Cwidth][.precision] A floating point number, optionally with a fixed precision. If precision is negative, there are no padding decimal zeros. .3f turns 1.0 into 1.000.
d [Cwidth][.precision] Behaves like i by default, but could also work like u or f if the tag is known.
c [count] A single character, optionally repeated count times. 3c produces AAA from 'A'.
b [01[.width]] A bitset, using the specified 0 character for zeroes and the 1 character for ones. The total width is 32 or width, if specified. AB.8b produces AAAABBBB from 7.
m [$][Cwidth][.precision] A monetary value, interpreted using the current locale, including the currency symbol. If $ is used, the currency symbol is local, otherwise an international abbreviation is used. Other values are interpreted like for i, with the difference that if precision is omitted, the smallest non-fractional unit for the currency is assumed. For en-US, the specifiers m, $m, $.0m, $.1m for the input 1234 result in USD12.34, $12.34, $1234.00, $123.40, respectively.
t format[+hours[:minutes]] A Unix timestamp (returned by gettime) formatted as time in the current locale. Format must be valid according to std::put_time, and may optionally be followed by a UTC time zone offset (starting with + or -) in hours, and optionally also minutes. If the UTC offset is not specified, the time is interpreted in the local time zone.
s [Cwidth][.size] An unpacked or packed string, optionally padded with C to a specified width, but not longer than size. .144s trims the input to the maximum chat line length.
S [Cwidth][.size] A valid PawnPlus dynamic string; the options are the same as for s.
q or e [C[chars]] An escaped unpacked or packed string, with C as the escape character (\ is default), escaping all occurrences of characters in chars (\ and ' by default). %0$%%{}e escapes all occurrences of %, {, or }.
Q or E [C[chars]] An escaped PawnPlus dynamic string; the options are the same as for q or e.
v A value with an unspecified type.
V options A valid PawnPlus variant. If the variant denotes a value that can be represented by a format selector, the selector is used instead of V together with options. If options do not constitute valid options for the inner selector and they end on a letter that denotes a selector which would otherwise be valid in combination with the rest of the options, that combination is used instead. Both .3V and .3fV for var_new(1.00) act like .3f for 1.00.
P options The result of pawn_arg_pack. Works like V.

Locale

It is possible to set the current locale or encoding using the special syntax {$enc:identifier}, passing in a valid locale/encoding identifier. This sequence may be placed in any location in the format string outside a format item or another sequence in braces, even multiple times, to affect all selectors that follow. Use {$enc:} to reset the locale to its initial state (the global one).

The effects of setting a locale (whether through the special item or via pp_locale) are:

  • For i, f, and other numeric selectors (using locale_numeric), the number is formatted in a locale-specific format, such as by grouping decimal digits and including separators, or using a different decimal point character.
  • For s, q, S, Q (using locale_ctype) the input string is converted from the default encoding to the set encoding, as if by str_convert.
  • For m (using locale_monetary), the number is formatted per the locale rules, and uses the currency used by the locale.
  • For t (using locale_time), the result uses locale-specific number and time formatting and preferences.
  • For V and P, the locale affects the formatting of the value stored inside as if used directly.

Custom format specifiers

All selectors (with the exception of P) are associated with a specific tag and also serve to distinguish the form of the string within a particular tag (for example, S, Q and E are all associated with the String: tag). When the tag is retrieved, its format operation is used to obtain the string, or string if it fails.

Both parts of the process can be configured separately. For example, a selector may be registered for an already existing tag:

str_register_format('n', tag_uid_handle);
str_format("%n", handle_new(1)); //<1>

Even though the Handle: tag is not aware of what the n selector does, it assumes the default format is desired if there are no parameters other than n. However, something like %03n would fail as the format is not understood by the tag.

This can be mitigated by creating a new tag and registering tag operations that handle the call:

forward String:PlayerNameFormat(playerid, type, String:format);
public String:PlayerNameFormat(playerid, type, String:format)
{
    if(!IsPlayerConnected(playerid)) return STRING_NULL;
    new name[MAX_PLAYER_NAME+1];
    GetPlayerName(playerid, name, sizeof name);
    return str_new(name);
}

new tag_uid:uid = tag_new("PlayerName");
tag_set_op(uid, tag_op_format, "PlayerNameFormat");
tag_lock(uid);
if(!str_register_format('p', uid))
{
    printf("Could not register the format operation to tag %d.", uid);
}

str_format("%p", 0); //name of the player
Clone this wiki locally