Formatted Output
Introduction
Scribe has functions to create and print formatted strings.
The functions build on Lua’s string.format
capabilities by adding extra format specifiers for Lua tables.
Method | Brief Description |
---|---|
scribe.format(fmt,...) |
Builds a string from a template fmt along with trailing values that fill the placeholders in fmt . |
scribe.put(fmt,...) |
Writes a formatted string from scribe.format to stdout . |
scribe.putln(fmt,...) |
Writes a formatted string followed by a newline to stdout . |
scribe.eput(fmt,...) |
Writes a formatted string to stderr . |
scribe.eput(fmt,...) |
Writes a formatted string followed by a newline to stderr . |
scribe.fput(f, fmt,...) |
Writes a formatted string to the file f . |
scribe.fput(f, fmt,...) |
Writes a formatted string followed by a newline to the file f . |
The scribe.format(fmt, ...)
method extends Lua’s standard string.format(fmt, ...)
. If the fmt
“recipe” doesn’t have any of Scribe’s custom table specifiers, then the two methods produce identical results.
Lua’s standard string.format
method is based almost entirely on the time-tested [sprintf] function from C
. You can use string.format
to do things like:
local name, age = "Johnny", 11
local greeting = string.format("Hi, my name is %s, and I am %d years old!", name, age)
print(greeting)
This prints “Hi, my name is Johnny, and I am 11 years old!” on the screen.
The template "Hi, my name is %s and I am %d years old!"
can be viewed as a recipe for making strings. The %s
and %d
format specifiers are placeholders and tell string.format
to look for two variables in the trailing arguments and to format the first as a string and the second as a decimal number. If you don’t pass two appropriate trailing parameters after the template, string.format
will be upset and raise an error.
Beyond the most trivial scripts, Lua programs are chock full of tables. A considerable drawback of string.format
is that it does not know how to format a Lua table usefully. The underlying C
mechanism it relies on is oblivious to its internals.
At best, given a table argument, string.format
will pop out something like “table:x600001d32980”, which can be interpreted as Lua saying, “Yes, I see a table, and it resides at that long hexadecimal address in memory”. That is very unlikely to be what you want.
Of course, the string.format
function is still very valuable. But to use it, you really need to unpack any table arguments into their constituent non-table elements. While this is always possible, it is ungainly and time-consuming.
The scribe.format
function fixes the gap by adding extra placeholder specifiers that let you output tables in several styles. When scribe.format
comes across %t
in a recipe template, it expects there is a corresponding table argument that you want to see as an inline table. The actual conversion of the table to a string is then done by scribe.inline
.
Table Format Specifiers
As well as all the usual Lua format specifiers like %s
for strings, Scribe recognises extra ones to deal with tables.
Specifier | Output from the Corresponding Table Argument |
---|---|
%t |
A one-line string. Arrays are delimited as [ ... ] . Other tables are delimited as { ... } . |
%T |
A pretty, multi-line string. Arrays are delimited as [ ... ] . Other tables are delimited as { ... } . Simple tables with no sub-tables are still inlined. |
%2T |
A “classic” multi-line string with tables in braces and all elements on separate indented lines. |
%3T |
A multi-line string without delimiters that shows the table structure by indentation alone. |
%9T |
A debug string showing the table structure in an abstract syntax tree. |
%J |
A multiline JSON string. |
%j |
A compact one-line JSON string. |
Example:
local putln = require('scribe').putln
local pupils = {{name = 'Mary', age = 12}, {name = 'Joe', age = 11}}
("Our pupils: %t", pupils) putln
Output:
Our pupils: [ { age = 12, name = "Mary" }, { age = 11, name = "Joe" } ]
Custom Stringification Methods
If your table has custom inline
, pretty
, classic
, alt
, json
, inline_json
or debug
methods, then scribe.format
, and the other scribe
methods like scribe.putln
, will use those methods to format the table.
For example, if you have a class like this:
local Pupil = {}
Pupil.__index = Pupil
function Pupil:new(name, age)
local self = setmetatable({}, Pupil)
self.name = name
self.age = age
return self
end
Then, the default scribe.format
methods will be used to print the table:
local pupil = Pupil:new("Mary", 12)
("%t", pupil) putln
Outputs:
{ age = 12, name = "Mary" }
Once we add a custom inline
method to the class:
function Pupil:inline()
return string.format("Pupil: %s is aged %d", self.name, self.age)
end
Then, that inline
method will be used to print the table:
("%t", pupil) putln
Outputs:
Pupil: Mary is aged 12
The custom methods must be named inline , pretty , classic , alt , json , inline_json or debug . You don’t need to implement all of them. In fact, it is common to implement the ones you need by using the scribe.format with custom option tables. You just need to be careful to avoid infinite loops by setting the use_metatable option to false where appropriate.
|
String & File Extensions
Scribe also adds some methods to Lua’s file
and string
classes.
If f
is a Lua file handle, and s
is a Lua string, then after loading scribe
you have access to the following new class methods:
Method | Brief Description |
---|---|
f:put(fmt, ...) |
This is another way of writing scribe.fput(f, fmt, ...) . |
f:putln(fmt, ...) |
This is another way of writing scribe.fputln(f, fmt, ...) . |
s:scribe(...) |
This is another way of writing scribe.format(s, ...) . |
Errors
Under the covers, scribe.format
processes table arguments into strings and then relies on string.format
to finish the formatting request. This means that scribe.format
can raise the same errors as string.format
.
For example, if you forget to pass a trailing argument:
("My name is %s, and I am %d years old.", "Tom") putln
Then, the program will crash with an error message that looks like:
.../scribe.lua:577: bad argument #3 to 'format' (no value)
stack traceback:
[C]: in function 'string.format'
.../scribe.lua:577: in function 'scribe.format'
.../scribe.lua:642: in function 'scribe.putln'
...
[C]: in ?
In addition, scribe.format
detects invalid placeholders in the template recipe. When that happens, the program does not crash. Instead, it returns a string that should make that programming error clear.
For example if you have:
("The table is %7t.", {1,2,3}) putln
This will output the error string:
[FORMAT ERROR]: "The table: %7t" -- unknown table specifier: "7t"