Skip to content

LISP - 流

Common Lisp 中的输入/输出(I/O)通过**流(streams)**处理。流是数据的源或汇,例如控制台、文件,甚至是字符串。这为与不同类型的数据源交互提供了统一的 API。

Common Lisp 提供了几个始终可用的标准流:

  • *standard-input*:默认输入流,通常连接到键盘。
  • *standard-output*:默认输出流,通常连接到控制台显示器。
  • *error-output*:用于打印错误消息和警告。
  • *trace-output*:用于跟踪函数执行。

尽管存在 write 和 write-line 等函数,但 format 函数是生成格式化输出最强大、最通用的工具。它的第一个参数是目标流(t 表示 *standard-output*),第二个参数是带有指令的控制字符串。

  • ~a (美观 Aesthetic):以人类可读的方式打印对象。
  • ~s (S-表达式 S-expression):以机器可读的 Lisp 语法打印对象。
  • ~d (十进制 Decimal):以十进制打印整数。
  • ~%:打印换行符。
  • ~~:打印字面波浪号 ~。
(let ((name "World") (n 42))
(format t "Hello, ~a! The number is ~d.~%" name n))
; Output: Hello, World! The number is 42.

您还可以通过使用 nil 作为目标流来使用 format 创建字符串:

(let ((message (format nil "User ~a logged in." "Alice")))
(print message))
; Output: "User Alice logged in."

文件 I/O 的最佳实践是使用 with-open-file 宏。它会自动打开文件,将流绑定到变量,最重要的是,即使发生错误,也能确保流在之后被关闭。

(defun file-io-example ()
(let ((filename "greetings.txt"))
;; 写入文件
(with-open-file (out-stream filename
:direction :output
:if-exists :supersede)
(format out-stream "Hello from Lisp!~%")
(format out-stream "The year is ~d.~%" 2023))
(format t "File '~a' written successfully.~%~%" filename)
;; 从文件中读取并打印每一行
(format t "Reading from file:~%")
(with-open-file (in-stream filename
:direction :input)
(loop for line = (read-line in-stream nil nil)
while line
do (format t " -> ~a~%" line)))))
;; 运行示例
(file-io-example)

您可以将文件操作包装在 handler-case 中,以优雅地处理潜在错误,例如文件未找到。

(handler-case
(with-open-file (stream "non-existent-file.txt"))
(file-error (c)
(format *error-output* "Caught a file error: ~a~%" c)))

字符串流允许您将字符串视为文件。这对于从字符串中解析数据或在内存中构建复杂字符串非常有用。

(let ((data-string "user-id 12345"))
(with-input-from-string (s data-string)
(let ((header (read s))
(id (read s)))
(format t "Header: ~s, ID: ~s~%" header id))))
; Output: Header: USER-ID, ID: 12345

Gray Streams 是 Common Lisp 的一个标准化扩展,它允许您实现自己的自定义流类。这是一项高级功能,用于创建压缩数据、通过网络通信或与自定义硬件交互的流等任务。对于大多数应用程序而言,内置流类型已足够。