Creating Format Strings in Lua

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