LISP - 字符串 I/O
现代 Lisp - 字符串 I/O
Section titled “现代 Lisp - 字符串 I/O”字符串 I/O 指的是将字符串视为数据源或接收器的操作。这是 Lisp 中一个强大的特性,可以轻松实现数据序列化、解析和动态字符串构建。这对于 Web 开发、配置文件管理和生成格式化文本至关重要。
从字符串读取:创建输入流
Section titled “从字符串读取:创建输入流”Common Lisp 提供了两种主要方法来将字符串视为输入源。
with-input-from-string 宏
Section titled “with-input-from-string 宏”这是最常用且灵活的方法。它从字符串创建一个临时字符输入流,允许你对其使用标准流函数,如 read、read-line 和 read-char。当宏的主体退出时,流会自动关闭。
;; 示例:解析一串数字的字符串(let ((data-string "10 20 30.5 an-atom")) (with-input-from-string (s data-string) (loop for item = (read s nil :eof) until (eq item :eof) do (format t "Read item: ~a (Type: ~a)~%" item (type-of item)))))Read item: 10 (Type: (INTEGER 0 4611686018427387903))Read item: 20 (Type: (INTEGER 0 4611686018427387903))Read item: 30.5 (Type: SINGLE-FLOAT)Read item: AN-ATOM (Type: SYMBOL)(read s nil :eof) 模式是 Lisp 中读取对象直到流末尾的惯用方式。
read-from-string 函数
Section titled “read-from-string 函数”此函数从字符串中解析一个 Lisp 对象。它对于快速解析很有用,但不如完整的流灵活。
安全警告:read 和 read-from-string 之所以强大,是因为它们使用了 Lisp 读取器,该读取器可以执行代码(例如,#.(+ 1 2))。**切勿在不受信任的用户输入上使用它们。**对于不受信任的数据,请使用 read-line 逐行解析并安全处理它。
(let* ((str "(list 1 2 3) and more") (obj (read-from-string str))) (print obj))
;; 它还返回读取停止的位置。(multiple-value-bind (expr pos) (read-from-string str) (format t "Expression: ~s~%Stopped at index: ~d~%" expr pos))(LIST 1 2 3)Expression: (LIST 1 2 3)Stopped at index: 13写入字符串:创建输出流
Section titled “写入字符串:创建输出流”类似地,Lisp 提供了优雅的方式来将输出捕获到字符串中。
format 函数
Section titled “format 函数”format 是 Lisp 多功能的打印和字符串格式化利器。通过将 nil 作为其第一个参数,它会返回格式化后的结果作为新字符串,而不是打印到流中。
(let* ((name "Alice") (items 3) (message (format nil "Hello, ~a! You have ~d item~:p in your cart." name items))) (print message))
;; 演示 1 个项目以显示复数指令 `~:p`(let ((message-singular (format nil "Hello, Bob! You have ~d item~:p." 1))) (print message-singular))"Hello, Alice! You have 3 items in your cart.""Hello, Bob! You have 1 item."with-output-to-string 宏
Section titled “with-output-to-string 宏”当你需要通过多次 print、write 或 format 调用增量构建字符串时,此宏是完美的工具。它会创建一个临时字符输出流,并将写入其中的所有内容作为单个字符串返回。
;; 示例:生成一个简单的 HTML 列表(let ((items '("Apples" "Bananas" "Cherries"))) (let ((html (with-output-to-string (s) (write-string "<ul>~%" s) (dolist (item items) (format s " <li>~a</li>~%" item)) (write-string "</ul>" s)))) (print html)))"<ul> <li>Apples</li> <li>Bananas</li> <li>Cherries</li></ul>"实际应用和最佳实践
Section titled “实际应用和最佳实践”readvs.read-line:当你的字符串包含要直接解析的有效 Lisp 数据结构时,使用read。当处理纯文本、面向行的数据(如日志或 CSV 文件)或不受信任的输入时,使用read-line。- 性能:对于在循环中构建复杂的字符串,
with-output-to-string通常比重复的字符串连接更高效。 - 集成:Lisp 基于流的 I/O 的美妙之处在于其统一性。一个接受流参数的函数可以无需修改地处理文件、字符串或网络连接。这使得代码具有高度的可重用性。
- 安全第一:再次重申安全警告:切勿将来自外部源(如 Web 请求)的原始、未清理的输入传递给
read或read-from-string。