We have already learned that Lua strings are immutable, and that changing a string produces a copy of the string which can make string construction through concatenation inefficient. String buffers can provide a more efficient means of constructing a string from one or more sub-strings in many situations. Lua format strings are another option that can provide a flexible and more efficient alternative.
Format strings are strings that contain "placeholders" that identify where values are to be inserted, and a simple "format specifier" that defines how each value should be formatted. These strings are then passed to the string.format library function with the values to be used to render the final string.
Let's look at an example:
print(string.format("%s %d %f", "hello", 123, math.pi)) -- hello 123 3.141593
This example used a simple template, consisting of placeholders %s
, %d
, and %f
, which were
populated by the values "hello"
, 123
, and math.pi
, respectively.
Placeholders are defined by %
, followed by the format specifier that has the format:
%[flags][width][.precision]type
where specifiers in brackets are optional, and behave slightly differently depending on the type
specifier. Learning to use Format strings effectively takes some experience, trial, and error, so
we will touch on the highlights and provide examples to show some common use-cases.
Type Specifier
The type
specifier indicates how the corresponding value should be formatted. Most of the type
options relate to numerical values, and render them in various
ways:
Type | Meaning | Example | Value | Result |
---|---|---|---|---|
s | String | %s |
abc |
abc |
d or i | Decimal or integer | %d |
123 |
123 |
f | Float | %f |
math.pi |
3.141593 |
e | Scientific notation | %e |
math.pi |
3.141593e+00 |
E | Scientific notation | %E |
math.pi |
3.141593E+00 |
g | Autoformat | %g |
math.pi |
3.141593 |
G | Autoformat | %G |
math.pi |
3.141593 |
o | Octal | %o |
123 |
173 |
x | Hexadecimal | %x |
123 |
7b |
c | Character | %c |
97 |
a |
q | Lua code | %q |
"abc" |
"abc" |
Two Scientific notation options are available, with the difference being whether e
or E
are
used to identify the exponent. There are also two Autoformat options, which evaluate the value
and automatically determine whether to format the value as a float or using scientific notation.
The two autoformat options differ in how they format the value when scientific notation is used,
where g
and G
correspond to e
and E
format, respectively. Character formatting converts a
character code (typically an ASCII code) into the corresponding character, and behaves like
the string.char library function. Finally, the q
type specified is a
Lua-specific option that renders the value in a format that can be parsed and executed by Lua.
Width
The width field specifies the minimum number of characters of width the value should occupy. If
the value requires fewer than the specified number of characters, then this many characters are
occupied. If the value requires more characters, then use the required number of characters. We can
see how this behaves by formatting math.pi
to a range of widths:
print(string.format("%1f", math.pi)) -- 3.141593
print(string.format("%2f", math.pi)) -- 3.141593
print(string.format("%3f", math.pi)) -- 3.141593
print(string.format("%4f", math.pi)) -- 3.141593
print(string.format("%5f", math.pi)) -- 3.141593
print(string.format("%6f", math.pi)) -- 3.141593
print(string.format("%7f", math.pi)) -- 3.141593
print(string.format("%8f", math.pi)) -- 3.141593
print(string.format("%9f", math.pi)) -- 3.141593
print(string.format("%10f", math.pi)) -- 3.141593
print(string.format("%11f", math.pi)) -- 3.141593
Not much happens when small width values are specified, then as the width exceeds 8 characters the value becomes "padded" with empty spaces. This is the default behavior, though we can change that with the Flags specifier.
Flags
As we saw in the previous section, when a value requires fewer characters of width that are specified the value becomes "left-padded" with spaces. Format strings provide a few flags that allow this behavior to be changed:
Flag | Meaning | Example | Value | Result |
---|---|---|---|---|
None | Default | %4d |
1 |
···1 |
- | Left Align | %-4d |
1 |
1··· |
+ | Show both + and - signs | %+4d |
1 |
··+1 |
0 | When width is present, zero-pad the value. |
%04d |
1 |
0001 |
space | prepend a space (" " ) for positive values |
% 4d |
1 |
···1 |
Precision
The precision field usually specifies a maximum limit of the output, depending on the particular formatting type.
The precision specifier defines the maximum width of the output, which is accomplished by rounding the value to the right of the decimal point to the specified number of digits:
print(string.format("%4.0f", math.pi)) -- 3
print(string.format("%4.1f", math.pi)) -- 3.1
print(string.format("%4.2f", math.pi)) -- 3.14
print(string.format("%4.3f", math.pi)) -- 3.142
print(string.format("%4.4f", math.pi)) -- 3.1416
print(string.format("%4.5f", math.pi)) -- 3.14159
print(string.format("%4.6f", math.pi)) -- 3.141593
print(string.format("%4.7f", math.pi)) -- 3.1415927
print(string.format("%4.8f", math.pi)) -- 3.14159265
print(string.format("%4.9f", math.pi)) -- 3.141592654
print(string.format("%4.10f", math.pi)) -- 3.1415926536
print(string.format("%4.11f", math.pi)) -- 3.14159265359