Книга: Practical Common Lisp
Other Kinds of I/O
Other Kinds of I/O
In addition to file streams, Common Lisp supports other kinds of streams, which can also be used with the various reading, writing, and printing I/O functions. For instance, you can read data from, or write data to, a string using
STRING-STREAMs, which you can create with the functions
MAKE-STRING-INPUT-STREAM takes a string and optional start and end indices to bound the area of the string from which data should be read and returns a character stream that you can pass to any of the character-based input functions such as
READ. For example, if you have a string containing a floating-point literal in Common Lisp's syntax, you can convert it to a float like this:
(let ((s (make-string-input-stream "1.23")))
(unwind-protect (read s)
MAKE-STRING-OUTPUT-STREAM creates a stream you can use with
WRITE-LINE, and so on. It takes no arguments. Whatever you write, a string output stream will be accumulated into a string that can then be obtained with the function
GET-OUTPUT-STREAM-STRING. Each time you call
GET-OUTPUT-STREAM-STRING, the stream's internal string is cleared so you can reuse an existing string output stream.
However, you'll rarely use these functions directly, because the macros
WITH-OUTPUT-TO-STRING provide a more convenient interface.
WITH-INPUT-FROM-STRING is similar to
WITH-OPEN-FILE—it creates a string input stream from a given string and then executes the forms in its body with the stream bound to the variable you provide. For instance, instead of the
LET form with the explicit
UNWIND-PROTECT, you'd probably write this:
(with-input-from-string (s "1.23")
WITH-OUTPUT-TO-STRING macro is similar: it binds a newly created string output stream to a variable you name and then executes its body. After all the body forms have been executed,
WITH-OUTPUT-TO-STRING returns the value that would be returned by
CL-USER> (with-output-to-string (out)
(format out "hello, world ")
(format out "~s" (list 1 2 3)))
"hello, world (1 2 3)"
The other kinds of streams defined in the language standard provide various kinds of stream "plumbing," allowing you to plug together streams in almost any configuration. A
BROADCAST-STREAM is an output stream that sends any data written to it to a set of output streams provided as arguments to its constructor function,
MAKE-BROADCAST-STREAM. Conversely, a
CONCATENATED-STREAM is an input stream that takes its input from a set of input streams, moving from stream to stream as it hits the end of each stream.
CONCATENATED-STREAMs are constructed with the function
MAKE-CONCATENATED-STREAM, which takes any number of input streams as arguments.
Two kinds of bidirectional streams that can plug together streams in a couple ways are
ECHO-STREAM. Their constructor functions,
MAKE-ECHO-STREAM, both take two arguments, an input stream and an output stream, and return a stream of the appropriate type, which you can use with both input and output functions.
TWO-WAY-STREAM every read you perform will return data read from the underlying input stream, and every write will send data to the underlying output stream. An
ECHO-STREAM works essentially the same way except that all the data read from the underlying input stream is also echoed to the output stream. Thus, the output stream of an
ECHO-STREAM stream will contain a transcript of both sides of the conversation.
Using these five kinds of streams, you can build almost any topology of stream plumbing you want.
Finally, although the Common Lisp standard doesn't say anything about networking APIs, most implementations support socket programming and typically implement sockets as another kind of stream, so you can use all the regular I/O functions with them.
Now you're ready to move on to building a library that smoothes over some of the differences between how the basic pathname functions behave in different Common Lisp implementations.