Skip to content

LISP - 字符串替换

字符串操作是大多数应用程序的基石。在 Common Lisp 中,字符串是不可变的字符序列。这意味着任何“替换”字符串部分的功能实际上都会返回一个带有修改的新字符串。本指南涵盖了内置函数以及使用正则表达式的现代标准方法。

使用 substitute 进行基本字符替换

Section titled “使用 substitute 进行基本字符替换”

最简单的替换工具是 substitute。它将特定字符的所有出现替换为新字符。

(substitute #\_ #\Space "a file name with spaces")
;=> "a_file_name_with_spaces"
(substitute #\o #\i "Lisp is interesting.")
;=> "Losp os onterestong."

你也可以使用其更强大的变体 substitute-if 和 substitute-if-not,配合谓词函数使用。

;; 将所有数字字符替换为星号 '*'
(substitute #\* #\Space "user-id-12345-tmp"
:test (lambda (item seq-item) (digit-char-p seq-item)))
;; 使用 substitute-if 更惯用的方式
(substitute-if #\* #'digit-char-p "user-id-12345-tmp")
;=> "user-id-*****-tmp"

行业标准:使用正则表达式进行子字符串替换

Section titled “行业标准:使用正则表达式进行子字符串替换”

对于任何比单个字符替换更复杂的操作,Common Lisp 社区的事实标准是 cl-ppcre 库。它提供了强大而高效的正则表达式工具。虽然你可以编写手动子字符串替换函数,但在生产代码中这通常不是一个好主意,因为性能不佳且复杂性高。

Quicklisp 是 Common Lisp 的包管理器。如果你尚未安装它,应该首先安装。安装后,你可以通过一个命令加载任何库。

;; 为当前会话加载库:
(ql:quickload "cl-ppcre")

你将使用的最常用函数是 cl-ppcre:regex-replace-all。

(ql:quickload "cl-ppcre")
;; 简单子字符串替换
(cl-ppcre:regex-replace-all "brown" "A quick brown fox" "red")
;=> "A quick red fox"
;; 使用正则表达式将多个空格替换为单个空格
(cl-ppcre:regex-replace-all "\\s+" "hello world from Lisp" " ")
;=> "hello world from Lisp"
;; 更复杂的替换:将日期从 YYYY-MM-DD 重新格式化为 MM/DD/YYYY
;; 我们使用捕获组 \1, \2, \3 来引用匹配的部分。
(cl-ppcre:regex-replace-all "(\\d{4})-(\\d{2})-(\\d{2})"
"Date: 2023-10-26"
"\\2/\\3/\\1")
;=> "Date: 10/26/2023"

用于学习:一个手动子字符串替换函数

Section titled “用于学习:一个手动子字符串替换函数”

为了理解底层逻辑,查看手动替换函数的工作原理可能会很有用。以下函数将替换子字符串的所有出现。这是一个很好的练习,但它也说明了为什么使用像 cl-ppcre 这样的库更受青睐,因为它具有健壮性和高性能。

(defun replace-all (string old new)
"将字符串 `string` 中 `old` 的所有出现替换为 `new`。
注意:对于生产代码,请使用 cl-ppcre:regex-replace-all。"
(with-output-to-string (s)
(loop with old-len = (length old)
with str-len = (length string)
for start = 0 then (+ pos old-len)
for pos = (search old string :start2 start)
do (write-string string s :start start :end pos)
when pos do (write-string new s)
while pos)))
;; --- 测试它 ---
(replace-all "A quick brown fox jumps over the brown dog" "brown" "red")
;=> "A quick red fox jumps over the red dog"
  • 默认使用 cl-ppcre:对于任何非简单的字符串替换,请使用 cl-ppcre。它快速、强大,并且是社区标准。
  • 理解不可变性:请记住,你总是在创建新字符串。在紧密循环中,请注意内存分配。如 replace-all 示例所示,使用 with-output-to-string 来高效地构建字符串。
  • 学习基本正则表达式:投入少量时间学习正则表达式将在你有效操纵文本的能力上获得巨大回报。