The VikiD Truth

Knowledge base / Computing / Lisp family

commonlisp | Functions | with-output-to-string

with-output-to-string

Here is the truth about with-output-to-string: it lets any code that “prints” produce a string instead, without changing that code at all. Printing and building strings turn out to be the same job pointed at different places.

What it is, in plain words

In Common Lisp, output goes to a stream. *standard-output* is usually your terminal, but a stream can just as well be a file, a socket or a growing string in memory. with-output-to-string creates a string output stream, binds a variable to it, runs the body, and returns everything written to it as a fresh string.

The variable can be any name you like. The very common idiom (with-output-to-string (*standard-output*) ...) rebinds standard output itself, so even plain (print ...) and (format t ...) calls inside the body get captured.

Why it matters

An everyday analogy: a dictation machine

You can say a letter out loud to a room (print to the terminal) or say exactly the same words into a dictaphone (a string stream). The words do not change. At the end you take the tape out and you have the letter, ready to be posted somewhere else.

A hands-on example

Try these in SBCL (sbcl on Vikix after xbps-install -S sbcl), ideally through SLY or SLIME in Emacs:

;; Write to the stream variable directly
(with-output-to-string (s)
  (write-string "Hello, " s)
  (format s "~a! You have ~d messages." "VikiD" 3))
;; => "Hello, VikiD! You have 3 messages."

;; Capture code that only knows how to print
(defun report (items)
  (dolist (i items)
    (format t "~&- ~a~%" i)))

(with-output-to-string (*standard-output*)
  (report '("lists" "symbols" "streams")))
;; => "- lists
;; - symbols
;; - streams
;; "

;; Its input-side twin
(with-input-from-string (in "10 20 30")
  (+ (read in) (read in) (read in)))
;; => 60

The second form is the trick worth remembering: rebinding *standard-output* captures output from code you did not write.

For one-off formatting, (format nil ...) is simpler: (format nil "~a-~a" 1 2) returns "1-2". Reach for with-output-to-string when the output is produced by several calls or by other functions.

The PicoLisp way. PicoLisp has no string streams, but you get the same effect two ways: pack joins values into a string directly, and pipe runs code that prints and lets you read the result back.

: (pack "Hello, " "VikiD" "! You have " 3 " messages.")
-> "Hello, VikiD! You have 3 messages."
: (pipe (prinl "captured") (line T))
-> "captured"

Common misconceptions

Connections

Reference

External Link: Macro WITH-OUTPUT-TO-STRING.

The macro WITH-OUTPUT-TO-STRING can be used to create a string output stream, and return the resulting string at the end.

with-output-to-string (var &optional string-form &key element-type)
declaration* form*

=> result*
element-type specifier is defaulted to character.
form* is implicit progn

with-output-to-string creates a character output stream and performs a series of operation that output to the result. automatically closed on exit side effect: the string is modified

(with-output-to-string (str)
  (write-line "Foobar!" str)
  (write-string "Barfoo!" str))
;=> "Foobar!
;   Barfoo!"
(let ((str (make-string-output-stream)))
  (write-line "Foobar!" str)
  (write-string "Barfoo!" str)
  (get-output-stream-string str))

Last edited · 4 revisions

Tags: #Computing