Skip to content

LISP - 访问字符串字符

在 Common Lisp 中,字符串是一维字符数组。这种结构允许通过索引高效、直接地访问任何字符。有几个标准函数可以执行这种访问,每个函数在使用和性能上都有细微的差异。

记住以下关键点:

  • 零基索引(Zero-Based Indexing):第一个字符的索引是 0,第二个是 1,依此类推。
  • 字符类型(Character Type):这些函数返回 character 类型的值,Lisp 使用 #\ 符号打印它(例如,#\A)。
函数描述最佳用途
char最通用和推荐的函数。它适用于任何类型的字符串。通用字符串访问。
scharchar 的一个特化版本,只适用于 simple-string。它可能更快,因为它避免了复杂字符串(例如,带有填充指针的字符串)所需的检查。性能关键的代码,当你确定字符串是 simple-string 时使用。
aref通用数组引用函数。由于字符串是数组,因此它可以用来访问字符。为此目的,它在功能上等同于 char。操作任何类型数组的通用代码,而不仅仅是字符串。

以下示例演示了如何使用这三个函数从字符串中访问字符。请注意,对于标准字符串字面量,它们的行为是相同的。

;; 使用 defparameter 定义一个全局的动态变量(一种常见约定)。
(defparameter *my-string* "Modern Lisp")
(format t "Original String: ~s~%~%" *my-string*)
;; --- 访问第一个字符(索引 0) ---
(format t "Using (char *my-string* 0): ~c~%" (char *my-string* 0))
(format t "Using (schar *my-string* 0): ~c~%" (schar *my-string* 0))
(format t "Using (aref *my-string* 0): ~c~%~%" (aref *my-string* 0))
;; --- 访问第七个字符(索引 6) ---
(let ((index 6))
(format t "Accessing character at index ~d:~%" index)
(format t "Character is: ~c~%" (char *my-string* index)))
Original String: "Modern Lisp"
Using (char *my-string* 0): M
Using (schar *my-string* 0): M
Using (aref *my-string* 0): M
Accessing character at index 6:
Character is: L
  • 差一错误(Off-by-One Error):忘记索引从 0 开始是一个常见错误。字符串 s 的最后一个字符的索引是 (- (length s) 1)。
  • 索引越界(Index Out of Bounds):尝试访问等于或大于字符串长度的索引将发出运行时错误信号。请务必确保你的索引在有效范围 [0, length-1] 内。
  • schar 的类型错误(Type Error with schar):如果你在一个复杂字符串(例如,使用填充指针创建的字符串)上使用 schar,将会发出 type-error 信号。除非你有特定的性能原因并且已验证字符串类型,否则请坚持使用 char。
;; 索引越界错误的示例
(let ((my-string "Hi"))
;; 这将导致错误,因为有效索引是 0 和 1。
(char my-string 2))

为了清晰、健壮和通用代码,优先使用 char。在现代 Lisp 实现中,schar 带来的潜在性能提升通常可以忽略不计,除非你正在对数百万个字符进行紧密循环操作。当编写旨在处理一般数组而不仅仅是字符串的代码时,请使用 aref。