流(stream)

Table of Contents

Common Lisp 的流对象有单向输入、单向输出和双向输出三种。

7 个标准流被称为标准初始流(standard initial streams)。

;;; *query-io*,双向流,一般用于询问用户情况下,用来代替 *standard-input* 和 *standard-output*

;;; peek-char,读取流的下一个字符,但这里不会改变流状态。
(with-open-file (stream #p"test")
  (loop for c = (peek-char nil stream nil)
     while c
       (progn
         (princ c)
         (read-char stream))))

;;; 读取文件内容(字符串返回)
;; 版本 1:
(defun read-file-content (filename)
  "读取指定文件的内容"
  (with-open-file (stream filename)
    (let ((data (make-string (file-length stream))))
      (read-sequence data stream)
      data)))
;; 版本 2:
(defun read-file-content (filename)
  (with-open-file (s filename)
    (with-output-to-string (out)
      (loop for line = (read-line s nil)
         while line
         do (write-string (format nil "~A~%" line) out)))))

;;; 逐行处理文件
(with-open-file (stream #p"/etc/passwd" :direction :input)
  (loop for line = (read-line stream nil)
     while line
     do
       (print line)))

1. force-output

(force-output &optional (stream *standard-output*))

在 stream 指向的缓存取的内容为空时不等待。

下面这段代码在 SBCL 下执行时,只有输入了内容才会看到提示字符“say:”:

(format t "say:")
(read)

注:CLISP 会先打印字符串,再等待输入,等同默认加了 force-output 操作。

force-output 可以在等待输入之前就看到输出的字符串:

(format t "say:")
(force-output)
(read)

2. input-stream-p

(input-stream-p stream)

判断 stream 是否是输入流对象。

(input-stream-p *standard-input*) ; => T
(input-stream-p *standard-output*) ; => NIL

3. output-stream-p

(output-stream-p stream)

判断 stream 是否是一个可输出的流对象,是则返回 t,否则返回 nil。

(output-stream-p *standard-input*)      ; => NIL
(output-stream-p *standard-output*)     ; => T

4. rename-file

(rename-file file new-name)

重命名文件

(rename-file #p"/tmp/old" "new")

5. ensure-directories-exist

(ensure-directories-exist path &key verbose (mode 511))

创建一个目录。path 指定的路径一定要以“/”结尾。

(ensure-directories-exist "/tmp/test-function/")
;; => "/tmp/test-function/"
;; T

;;;; 如果路径不以“/”结尾,目录 a 是不会被创建的
(ensure-directories-exist "/tmp/test-function/a")
;; => "/tmp/test-function/a"
;; NIL

6. with-standard-io-syntax

(with-standard-io-syntax &body body)

在执行 body 的表示式时,将一些会影响 I/O 输出的变量恢复成默认值,防止动态绑定的影响。

(let ((a '(1 2 3 4 5))
      ;; *print-length* 设置打印 list 时显示的元素数量
      (*print-length* 3))
  (print a) ; => (1 2 3 ...)
  (with-standard-io-syntax
    ;; 输出:(1 2 3 4 5),在 with-standard-io-syntax 代码块中,*print-length* 将恢复成默认值
    (print a)))