LISP - 文件 I/O
Lisp - 现代文件 I/O
Section titled “Lisp - 现代文件 I/O”Common Lisp 中的文件输入/输出(I/O)是通过一种称为流(streams)的抽象来处理的。流是数据的来源或目的地,它可以是文件、终端、网络连接,甚至是字符串。用于读写数据的相同函数,如 format 和 read-line,可以作用于任何类型的流。
本章涵盖了在 Common Lisp 中执行文件操作的现代、安全且地道(idiomatic)的方式。
正确方式:with-open-file
Section titled “正确方式:with-open-file”虽然 Lisp 提供了低级的 open 函数,但推荐的最佳实践是始终使用 with-open-file 宏(macro)。它自动化了关闭文件的关键步骤,即使在处理过程中发生错误也能确保关闭。这可以防止资源泄漏(resource leaks),是健壮编程(robust programming)的核心原则。
with-open-file 的语法是:
(with-open-file (stream-variable filename-or-path &key options...) ;; 在这里编写从流变量读取或写入的代码 ...);; 文件在此处自动关闭with-open-file 的关键选项包括:
:direction:指定流的用途。常见值有: *:input(默认值):用于从文件读取。 *:output:用于写入文件。 *:io:用于同时读写。:if-exists:当:direction为:output且文件已存在时如何处理。 *:supersede(默认值):覆盖现有文件。 *:append:将新内容添加到文件末尾。 *:error:发出错误信号。:if-does-not-exist:当文件不存在时如何处理。 *:create(:output的默认值):创建一个新文件。 *:error(:input的默认值):发出错误信号。 *nil:从with-open-file返回nil而不是发出错误信号。:element-type:正在传输的数据类型。对于文本文件,默认为character。对于二进制文件(binary files),使用(unsigned-byte 8)。:external-format:字符编码(character encoding)。明确指定此项是良好的实践。现代标准是:utf-8。
此示例将多行文本写入名为 notes.txt 的文件。如果文件存在,它将被覆盖。
(defun write-notes () (with-open-file (stream "notes.txt" :direction :output :if-exists :supersede :external-format :utf-8) (format stream "Welcome to a modern Lisp tutorial!~%") (format stream "File I/O is managed safely with with-open-file.~%") (format stream "The file stream is automatically closed.~%")) (format t "File 'notes.txt' has been written successfully.~%"))
(write-notes)运行此代码后,将在同一目录下创建一个名为 notes.txt 的文件,其中包含指定内容。format 中的 ~% 是打印换行符(newline character)的便捷方式。
现在,让我们从 notes.txt 中读取内容。我们将逐行读取文件直到文件末尾。
(defun read-notes () (with-open-file (stream "notes.txt" :if-does-not-exist nil) (if stream (loop for line = (read-line stream nil) while line do (format t "Read line: ~a~%" line)) (format t "File 'notes.txt' does not exist.~%"))))
(read-notes)在此示例中:
:if-does-not-exist nil可防止文件缺失时报错;with-open-file将简单地返回nil。(read-line stream nil)读取一行文本。第二个参数nil告诉它在文件末尾返回nil而不是发出错误信号。这个nil随后会使loop的while条件失败,从而优雅地终止循环。
如果你在写入示例之后运行此代码,输出将是:
Read line: Welcome to a modern Lisp tutorial!Read line: File I/O is managed safely with with-open-file.Read line: The file stream is automatically closed.关于路径名(Pathnames)的说明
Section titled “关于路径名(Pathnames)的说明”Common Lisp 有一种复杂且平台无关(platform-independent)的方式来处理文件路径,称为路径名(pathnames)。虽然像 "notes.txt" 这样的字符串通常有效,但使用路径名构造器 #p 会更健壮,特别是对于复杂路径。
;; 指定文件路径的更健壮方式(with-open-file (stream #p"notes.txt" :direction :input) ...)使用路径名允许 Lisp 自动处理目录分隔符(directory separators)和其他平台特定(platform-specific)的细节。