Skip to content

LISP - 字符串修剪

在 Common Lisp 中,从字符串的开头或结尾删除不需要的字符——这一过程被称为修剪(trimming)——是一项常见且直接的任务。该语言提供了一套强大、内置的函数,专为此目的设计。本更新教程将探讨这些函数、它们的现代用法和最佳实践。

Common Lisp 中字符串修剪的核心围绕三个主要函数展开。它们都通过将字符串中的字符与一个字符袋(即一个包含要删除字符的序列)进行比较来工作。

函数描述
string-trim接受一个字符袋和一个字符串。它返回一个新字符串,其中包含已从字符串的开头和结尾移除字符袋中所有字符。
string-left-trim类似于 string-trim,但只从字符串的开头(左侧)移除字符。
string-right-trim类似于 string-trim,但只从字符串的结尾(右侧)移除字符。

这些函数的第一个参数是一个字符序列,我们称之为“字符袋”。它不是要匹配的子字符串。相反,字符袋中存在的任何字符都将从目标字符串的两端被修剪掉。字符袋中字符的顺序无关紧要。

例如,如果字符袋是 "ab ",这些函数将从字符串两端修剪掉任何 ‘a’、‘b’ 或空格字符。

让我们看看这些函数的实际应用。以下代码演示了如何从字符串中修剪空格和其他字符。我们使用 format 进行输出,这是在 Lisp 中打印文本的一种高度灵活且符合语言习惯的方式。

(defun run-trimming-examples ()
"演示标准字符串修剪函数。"
(let ((my-string " a big hello from tutorials point ")
(whitespace-bag '(#\Space #\Tab #\Newline)))
(format t "Original: '~a'~%~%" my-string)
;; 从两端修剪空格
(format t "string-trim: '~a'~%"
(string-trim whitespace-bag my-string))
;; 只从左侧修剪空格
(format t "string-left-trim: '~a'~%"
(string-left-trim whitespace-bag my-string))
;; 只从右侧修剪空格
(format t "string-right-trim: '~a'~%"
(string-right-trim whitespace-bag my-string))
;; 使用不同字符袋的示例
(let ((another-string "...!!hello world!!...")
(punctuation-bag ".!"))
(format t "~%Trimming punctuation: '~a'~%"
(string-trim punctuation-bag another-string)))))
;; 运行示例
(run-trimming-examples)
Original: ' a big hello from tutorials point '
string-trim: 'a big hello from tutorials point'
string-left-trim: 'a big hello from tutorials point '
string-right-trim: ' a big hello from tutorials point'
Trimming punctuation: 'hello world'

进阶主题:一个自定义修剪函数

Section titled “进阶主题:一个自定义修剪函数”

虽然内置函数通常足够使用,但您可能偶尔需要根据条件而不是固定的字符集进行修剪。例如,您可能想要从字符串中修剪掉所有数字。这需要一个使用谓词的自定义函数。

下面是一个结构良好的自定义 trim-if 函数。请注意,它使用 position-if-not 来查找第一个不应该被修剪的字符。

(defun trim-if (predicate string)
"修剪字符串 STRING 两端,使 PREDICATE 返回 true 的字符。
示例:(trim-if #'digit-char-p \"123hello456\") => \"hello\""
(let* ((start (position-if-not predicate string))
(end (position-if-not predicate string :from-end t)))
(if start
(subseq string start (and end (1+ end)))
"")))
;; --- 示例用法 ---
;; 修剪所有数字字符
(format t "Custom trim (digits): '~a'~%"
(trim-if #'digit-char-p "123abc456"))
;; 修剪所有非字母字符
(format t "Custom trim (non-letters): '~a'~%"
(trim-if (lambda (c) (not (alpha-char-p c))) "--hello world!--"))
Custom trim (digits): 'abc'
Custom trim (non-letters): 'hello world'
  • 优先使用内置函数:尽可能始终使用标准的 string-trim、string-left-trim 和 string-right-trim。它们经过高度优化,并且是标准的一部分,使您的代码可移植且易读。
  • 字符袋与子字符串的区别:一个常见的错误是认为 (string-trim "abc" "abcdef") 会移除前缀 “abc”。它不会。它会移除两端所有 ‘a’、‘b’ 或 ‘c’ 字符。在这种情况下,结果是 "def"。
  • 性能:对于性能关键的代码,请记住这些函数会创建一个新字符串。在紧密循环中进行修剪可能会导致内存分配开销。如果性能是一个问题,请考虑替代的数据处理策略。