Throughout this book, we've shown some simple uses of FORMAT
without explanation. In this chapter, we'll inventory and describe
FORMAT
's most useful capabilities, and a few of its more
esoteric features.
FORMAT
appears to have been inspired by FORTRAN's
varied and capable function of the same name. But Lisp's
FORMAT
implements a programming language in its own
right, designed expressly for the purposes of formatting textual
output. FORMAT
can print data of many types, using
various decorations and embellishments. It can print numbers as
words or -- for you movie buffs -- as Roman numerals. Columnar
output is a breeze with FORMAT
's facilities for
iterating over lists and advancing to specific positions on an
output line. You can even make portions of the output appear
differently depending upon the formatted variables. This chapter
covers a representative portion of what FORMAT
can do;
you should consult a Lisp reference to get the full story.
FORMAT
expects a destination argument, a format
control string, and a list of zero or more arguments to be used by
the control string to produce formatted output.
Output goes to a location determined by the destination argument.
If the destination is T
, output goes to
*STANDARD-OUTPUT*
. The destination can also be a
specific output stream.
There are two ways FORMAT
can send output to a string.
One is to specify NIL
for the destination: FORMAT
will return a string containing the formatted output. The other way is to
specify a string for the destination; the string must have a fill pointer.
? (defparameter *s* (make-array 0 :element-type 'character :adjustable t :fill-pointer 0)) "" ? (format *s* "Hello~%") NIL ? *s* "Hello " ? (format *s* "Goodbye") NIL ? *s* "Hello Goodbye" ? (setf (fill-pointer *s*) 0) 0 ? *s* "" ? (format *s* "A new beginning") NIL ? *s* "A new beginning"
The call to MAKE-ARRAY
with options as shown above creates
an empty string that can expand to accommodate new output. As you can see,
formatting additional output to this string appends the new output to whatever
is already there. To empty the string, you can either reset its fill pointer
(as shown) or create a new emtpy string.
FORMAT
returns NIL
except when the destination
is NIL
.
The format control string contains literal text and formatting directives.
Directives are always introduced with a ~
character.
Directive Interpretation --------- -------------- ~% new line ~& fresh line ~| page break ~T tab stop ~< justification ~> terminate ~< ~C character ~( case conversion ~) terminate ~( ~D decimal integer ~B binary integer ~O octal integer ~X hexadecimal integer ~bR base-b integer ~R spell an integer ~P plural ~F floating point ~E scientific notation ~G ~F or ~E, depending upon magnitude ~$ monetary ~A legibly, without escapes ~S READably, with escapes ~~ ~
The first few directives in the table above are for generating whitespace.
A ~%
directive inserts a newline character. ~&
inserts a newline only if FORMAT
output is not already at the
beginning of a new line. ~|
generates a page break character;
not all devices are capable of responding to this character. You can cause
FORMAT
to emit multiple line or page breaks by using an optional
argument, as in ~5%
which generates five newlines.
To advance output to column n by inserting spaces, use
~
nT
.
You can justify output using ~<
and ~>
to
enclose the output to be justified.
~
w
<
text~>
right-justifies text in a field of width n. If you segment
text using ~;
as a separator, the segments will be
evenly distributed across the field of width w.
~C
formats a character. Normally, the character
formats as itself. However, if you modify the directive to ~:C
,
then non-printable characters are spelled out.
? (format nil "~:C" 7) ;; 7 is ASCII BEL "Bell"
You can change the alphabetic case by enclosing output in
~(
and ~)
. Different forms of the
directive produce different results.
~(
text~)
converts text to
lower case. ~:@(
text~)
converts
text to upper case.
~:(
text~)
capitalizes each word in
text. ~@(
text~)
capitalizes
the first word in text and converts the rest to lower case.
The ~D
directive formats an integer as decimal
digits with a leading minus sign if the number is negative.
~
wD
right-justifies the output in a
field of width w, padding on the left with spaces.
~
w,'cD
pads on the
left with the character c. Adding the @
modifier
immediately after the ~
causes a leading plus sign to
be emitted for positive numbers as well as the leading minus sign
that is always emitted for negative numbers. A :
modifier
causes commas to be inserted between each group of three digits.
You can format numbers in binary (base 2), octal (base 8) or
hexadecimal (base 16) radix using the directives ~B
,
~O
, and ~X
, respectively. Except for the
radix, these behave exactly as ~D
.
The ~R
directive has two forms.
~
bR
prints an integer in base
b. The directive ~10R
is identical to
~D
. Optional values should be specified following the
radix. For example, ~3,8R prints a base-3 number in a field
of width 8.
Without the radix specifier, ~R
spells out an integer as
a cardinal (counting) number in English.
? (format nil "~R" 7) "seven" ? (format nil "~R" 376) "three hundred seventy-six"
~:R
spells out an ordinal (positional) number.
? (format nil "~:R" 7) "seventh"
You can even print Roman numerals using ~@R
.
? (format nil "~@R" 1999) "MCMXCIX"
One of the hallmarks of professional-looking output is getting
plurals right. Lisp makes this easy with the ~P
format
directive. Unless its argument is 1, ~P
formats an "s".
~@P
formats a "y" if its argument is 1, or an "ies" if
the argument is not 1. Since these operations so commonly follow the
printing of a number, ~:P
and ~:@P
reuse
the previously consumed argument.
? (format nil "~D time~:P, ~D fl~:@P" 1 1) "1 time, 1 fly" ? (format nil "~D time~:P, ~D fl~:@P" 3 4) "3 times, 4 flies"
You can print floating-point numbers in four different ways:
~F
~E
~G
~$
There are many options for printing floating point numbers. The most
common is the field-width specifier, which behaves as it does in the
~D
directive.
Non-numeric Lisp data is printed using the ~A
and
~S
directives. Use ~A
when you want to print
data in its most visually attractive form; that is, without the escape
characters that would let the data be read correctly by READ
.
Using ~A
, strings are formatted without quotes, symbols are
formatted without package prefixes or escapes for mixed-case or blank
characters, and characters are printed as themselves. Using ~S
,
every Lisp object is printed in such a way that READ
can
reconstruct the object (unless, of course, the object does not have a
readable representation).
List elements can be formatted using the directive
~{
format-control~}
. The argument
must be a list; format-control consumes elements
of the list. Iteration terminates when the list is empty before
the next pass over format-control. Here's a simple example:
? (format t "~&Name~20TExtension~{~&~A~20T~A~}" '("Joe" 3215 "Mary" 3246 "Fred" 3222 "Dave" 3232 "Joseph" 3212)) Name Extension Joe 3215 Mary 3246 Fred 3222 Dave 3232 Joseph 3212 NIL
If your list might end in the middle of the iteration's
format-control, you can insert a ~^
directive at
that point. If the argument list is empty when interpreting the
~^
directive, the iteration
~{
format-control~}
terminates at
that point.
Additional options let you limit the number of iterations and specify different requirements for the arguments. Consult a Lisp reference for details.
Conditional format directives are introduced by ~[
and delimited by ~]
. There are several forms, which
I'll call ordinal, binary, and conditional. The ordinal form is
~[
format-0~;
format-1~;
...~;
format-N~]
,
which selects the format-Ith clause for an argument value of
I.
? (format t "~[Lisp 1.5~;MACLISP~;PSL~;Common Lisp~]" 2) PSL NIL
Within ~[
and ~]
you can specify a
final default clause as
~:;
format-default; this is selected if the argument
is outside the range 0 to N.
The binary form is written
~:[
format-false~;
format-true~]
.
The format-false clause is interpreted if the argument is
NIL
; otherwise, the format-true clause is
interpreted.
? (format t "My computer ~:[doesn't~;does~] like Lisp." t) My computer does like Lisp. NIL ? (format t "My computer ~:[doesn't~;does~] like Lisp." nil) My computer doesn't like Lisp. NIL
The conditional form, written as ~@[
format~]
, first tests its argument. If the argument is not NIL
,
it is not consumed; rather, it is left for format to consume. If
the argument is NIL
, then it is consumed and format is
not interpreted.
? (format nil "~{~@[~A ~]~}" '(1 2 nil 3 t nil 4 nil)) "1 2 3 T 4"
As you've seen, many of the format directives accept optional parameters,
such as the field width parameter of the justification, tabbing, and numeric
directives. Our examples have encoded these parameters into the format
control string. Sometimes it is useful to have a parameter vary during the
program's operation. You can do this by specifying V
where
the parameter would appear; the parameter's value is then taken from the
next argument in the argument list.
? (format t "~{~&~VD~}" '(5 37 10 253 15 9847 10 559 5 12)) 37 253 9847 559 12 NIL
In this example, the arguments are consumed in pairs, with the first of each pair specifying a field width and the second being the number to print.