Skip to content

LISP - 使用 `Format` 格式化

Common Lisp 中的 format 函数是文本格式化和输出的独特强大工具。它作用如同 Lisp 中的一种微型语言,能够精确控制文本呈现,生成简单或复杂的字符串。它远比简单的字符串连接或在其他语言中找到的 printf 功能强大。

(format destination control-string &rest arguments)
  • destination:输出目标。常见的值有 t(标准输出,即控制台)、nil(将格式化文本作为新字符串返回)或一个流。
  • control-string:包含纯文本和特殊格式指令的字符串。每个指令都以波浪号(~)开头。
  • arguments:要格式化的值,每个需要参数的指令对应一个。

以下是最重要的指令细分,按类别分组。

  • ~A (Aesthetic):以人类可读的格式打印一个参数。适用于大多数通用目的。(format nil "Hello, ~A!" "World") -> "Hello, World!"
  • ~S (S-Expression):以 Lisp 可读的格式(S-表达式)打印一个参数,字符串和符号会带引号。(format nil "The value is ~S." "a string") -> "The value is \"a string\"."
  • ~% (Newline):插入一个换行符。
  • ~~ (Tilde):插入一个字面量的波浪号字符。
  • ~D (Decimal):以十进制打印一个整数。
  • ~B (Binary):以二进制打印一个整数。
  • ~X (Hexadecimal):以十六进制(大写)打印一个整数。
  • ~F (Floating-point):打印一个浮点数。可以接受宽度和精度的参数。
  • ~R (Radix):以 2 到 36 之间的任何进制打印一个整数,或以英文单词形式打印(例如,(format nil "~R" 27) -> "twenty-seven")。

将 nil 用作目标非常适合以编程方式构建字符串。

(let ((user "Alice")
(items 5))
(let ((message (format nil "User ~S has ~D item(s) in their cart." user items)))
(print message)))
"User \"Alice\" has 5 item(s) in their cart."

指令可以接受前缀参数(mincol、padchar)来控制对齐和填充。格式为 ~<mincol>,<padchar><directive>。

;; 将数字 5 打印为二进制字符串,用 '0' 填充至最小宽度 8。
(format t "~8,'0B~%" 5)
;; 将字符串右对齐打印在一个 10 个字符宽的列中。
(format t "|~10A|~%" "test")
00000101
| test|
  • Structured Logging:format 非常适合创建详细、格式良好的日志消息。您可以轻松地包含时间戳、严重性级别和结构化数据。
  • Generating Reports:使用它来生成简单的文本报告、CSV 文件或其他基于行的传输格式。
  • Readability:对于复杂的格式化,特别是包含条件逻辑(~[)或迭代(~{)时,format 字符串可能变得难以阅读。考虑将复杂逻辑分解为辅助函数,并保持控制字符串相对简单。
  • Security Note:当将用户输入合并到 format 字符串中时,务必将其作为指令的参数(例如 ~A)传递。切勿从不受信任的输入构造 control-string 本身,因为这可能导致安全漏洞(格式字符串攻击)。

本教程仅仅触及了皮毛。Common Lisp Hyperspec 中关于 format 的条目是权威参考。探索高级指令,如用于列表迭代的 ~{...~} 和用于条件格式化的 ~[...~],以发挥其全部潜力。