(when in

(loop for line = (read-line in nil)

while line do (format t '~a~%' line))

(close in)))

Of the three text-reading functions, READ is unique to Lisp. This is the same function that provides the R in the REPL and that's used to read Lisp source code. Each time it's called, it reads a single s-expression, skipping whitespace and comments, and returns the Lisp object denoted by the s-expression. For instance, suppose /some/file/name.txt has the following contents:

(1 2 3)

456

'a string' ; this is a comment

((a b)

(c d))

In other words, it contains four s-expressions: a list of numbers, a number, a string, and a list of lists. You can read those expressions like this:

CL-USER> (defparameter *s* (open '/some/file/name.txt'))

*S*

CL-USER> (read *s*)

(1 2 3)

CL-USER> (read *s*)

456

CL-USER> (read *s*)

'a string'

CL-USER> (read *s*)

((A B) (C D))

CL-USER> (close *s*)

T

As you saw in Chapter 3, you can use PRINT to print Lisp objects in 'readable' form. Thus, whenever you need to store a bit of data in a file, PRINT and READ provide an easy way to do it without having to design a data format or write a parser. They even—as the previous example demonstrated—give you comments for free. And because s-expressions were designed to be human editable, it's also a fine format for things like configuration files.[151]

Reading Binary Data

By default OPEN returns character streams, which translate the underlying bytes to characters according to a particular character-encoding scheme.[152] To read the raw bytes, you need to pass OPEN an :element- type argument of '(unsigned-byte 8).[153] You can pass the resulting stream to the function READ-BYTE, which will return an integer between 0 and 255 each time it's called. READ-BYTE, like the character-reading functions, also accepts optional arguments to specify whether it should signal an error if called at the end of the file and what value to return if not. In Chapter 24 you'll build a library that allows you to conveniently read structured binary data using READ-BYTE.[154]

Bulk Reads

One last reading function, READ-SEQUENCE, works with both character and binary streams. You pass it a sequence (typically a vector) and a stream, and it attempts to fill the sequence with data from the stream. It returns the index of the first element of the sequence that wasn't filled or the length of the sequence if it was able to completely fill it. You can also pass :start and :end keyword arguments to specify a subsequence that should be filled instead. The sequence argument must be a type that can hold elements of the stream's element type. Since most operating systems support some form of block I/O, READ-SEQUENCE is likely to be quite a bit more efficient than filling a sequence by repeatedly calling READ-BYTE or READ-CHAR.

File Output

To write data to a file, you need an output stream, which you obtain by calling OPEN with a :direction keyword argument of :output. When opening a file for output, OPEN assumes the file shouldn't already exist and will signal an error if it does. However, you can change that behavior with the :if-exists keyword argument. Passing the value :supersede tells OPEN to replace the existing file. Passing :append causes OPEN to open the existing file such that new data will be written at the end of the file, while :overwrite returns a stream that will overwrite existing data starting from the beginning of the file. And passing NIL will cause OPEN to return NIL instead of a stream if the file already exists. A typical use of OPEN for output looks like this:

(open '/some/file/name.txt' :direction :output :if-exists :supersede)

Common Lisp also provides several functions for writing data: WRITE- CHAR writes a single character to the stream. WRITE-LINE writes a string followed by a newline, which will be output as the appropriate end-of-line character or characters for the platform. Another function, WRITE-STRING, writes a string without adding any end-of-line characters. Two different functions can print just a newline: TERPRI—short for 'terminate print'—unconditionally prints a newline character, and FRESH-LINE prints a newline character unless the stream is at the beginning of a line. FRESH-LINE is handy when you want to avoid spurious blank lines in textual output generated by different functions called in sequence. For example, suppose you have one function that generates output that should always be followed by a line break and another that should start on a new line. But assume that if the functions are called one after the other, you don't want a blank line between the two bits of output. If you use FRESH-LINE at the beginning of the second function, its output will always start on a new line, but if it's called right after the first, it won't emit an extra line break.

Several functions output Lisp data as s-expressions: PRINT prints an s- expression preceded by an end-of-line and followed by a space. PRIN1 prints just the s-expression. And the function PPRINT prints s-expressions like PRINT and PRIN1 but using the 'pretty printer,' which tries to print its output in an aesthetically pleasing way.

Вы читаете Practical Common Lisp
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату