Elisp 基本数据类型:字符和字符串

Table of Contents

字符

构成字符串的字符其实就是一个整数,目前字符串中的字符被限制在 0-524287 之间。字符的读入语法是在字符前加上一个问号,比如 ?A 代表字符 ’A’。

?A          ; => 65
?a          ; => 97

对于标点来说 ,也可以用同样的语法,但因为有些标点有歧义,比如 ?\(。

控制字符,退格、制表符,换行符,垂直制表符,换页符,空格,回车,删除和 escape 表示为 ?\a, ?\b, ?\t, ?\n, ?\v, ?\f, ?\s, ?\r, ?\d, 和 ?\e。对于没有特殊意义的字符,加上转义字符 \ 是没有副作用的,比如 ?\+ 和 ?+ 是完全一样的。所以标点还是都用转义字符来表示吧。

?\a => 7                 ; control-g, `C-g'
?\b => 8                 ; backspace, <BS>, `C-h'
?\t => 9                 ; tab, <TAB>, `C-i'
?\n => 10                ; newline, `C-j'
?\v => 11                ; vertical tab, `C-k'
?\f => 12                ; formfeed character, `C-l'
?\r => 13                ; carriage return, <RET>, `C-m'
?\e => 27                ; escape character, <ESC>, `C-['
?\s => 32                ; space character, <SPC>
?\\ => 92                ; backslash character, `\'
?\d => 127               ; delete character, <DEL>

控制字符可以有多种表达方式,比如 C-i:

?\^I  ?\^i  ?\C-I  ?\C-i

它们都对应数字 9。

修饰键

meta 字符是用 修饰键(通常就是 Alt 键)输入的字符。之所以称为修饰键,是因为这样输入的字符就是在其修饰字符的第 27 位由 0 变成 1 而成,也就是如下操作:

(logior (lsh 1 27) ?A)                  ; => 134217793
?\M-A                                   ; => 134217793

你可以用 \M- 代表 meta 键,加上修饰的字符就是新生成的字符。比如:?\M-A, ?\M-\C-b. 后面这个也可以写成 ?\C-\M-b。

如果你还记得前面说过字符串里的字符不能超过 524287 的话,这就可以看出字符串是不能放下一个 meta 字符的。所以按键序列在这时只能用 vector 来储存。

其它的修饰键也是类似的。emacs 用 2**25 位来表示 shift 键,2**24 对应 hyper,2**23 对应 super,2**22 对应 alt。

测试函数

字符串测试使用 stringp,没有 charp,因为字符就是整数。 string-or-null-p 当对象是一个字符或 nil 时返回 t。char-or-string-p 测试是否是字符串或者字符类型。比较头疼的是 emacs 没有测试字符串是否为空的函数。

(defun string-emptyp (str)
  (not (string< "" str)))

构造函数(产生一个字符串)

产生一个字符串可以用 make-string。这样生成的字符串包含的字符都是一样的。要生成不同的字符串可以用 string 函数。

(make-string 5 ?x)                      ; => "xxxxx"
(string ?a ?b ?c)                       ; => "abc"

在已有的字符串生成新的字符串的方法有 substring, concat。substring 的后两个参数是起点和终点的位置。如果终点越界或者终点比起点小都会产生一个错误。这个在使用 substring 时要特别小心。

(substring "0123456789" 3)              ; => "3456789"
(substring "0123456789" 3 5)            ; => "34"
(substring "0123456789" -3 -1)          ; => "78"

concat 函数相对简单,就是把几个字符串连接起来。

字符串比较

char-equal 可以比较两个字符是否相等。与整数比较不同,这个函数还考虑了大小写。如果 case-fold-search 变量是 t 时,这个函数的字符比较是忽略大小写的。编程时要小心,因为通常 case-fold-search 都是 t,这样如果要考虑字符的大小写时就不能用 char-equal 函数了。

字符串比较使用 string=,string-equal 是一个别名。

string< 是按字典序比较两个字符串,string-less 是它的别名。空字符串小于所有字符串,除了空字符串。前面 string-emptyp 就是用这个特性。当然直接用 length 检测字符串长度应该也可以,还可以省去检测字符串是否为空。没有 string> 函数。

转换函数

字符转换成字符串可以用 char-to-string 函数,字符串转换成字符可以用 string-to-char。当然只是返回字符串的第一个字符。

数字和字符串之间的转换可以用 number-to-string 和 string-to-number。其中 string-to-number 可以设置字符串的进制,可以从 2 到 16。number-to-string 只能转换成 10 进制的数字。如果要输出八进制或者十六进制,可以用 format 函数:

(string-to-number "256")                ; => 256
(number-to-string 256)                  ; => "256"
(format "%#o" 256)                      ; => "0400"
(format "%#x" 256)                      ; => "0x100"

concat 可以把一个字符构成的列表或者向量转换成字符串,vconcat 可以把一个字符串转换成一个向量,append 可以把一个字符串转换成一个列表。

(concat '(?a ?b ?c ?d ?e))              ; => "abcde"
(concat [?a ?b ?c ?d ?e])               ; => "abcde"
(vconcat "abdef")                       ; => [97 98 100 101 102]
(append "abcdef" nil)                   ; => (97 98 99 100 101 102)

格式化字符串

format 类似于 C 语言里的 printf 可以实现对象的字符串化。数字的格式化和 printf 的参数差不多,值得一提的是 “%S” 这j个格式化形式,它可以把对象的输出形式转换成字符串

字符串格式化:

(format "Hello, %s!" "Emacs") ; 返回 "Hello, Emacs!"

整数格式化:

(format "Count: %d" 42) ; 返回 "Count: 42"

浮点数格式化:

(format "Value: %.2f" 3.14159) ; 返回 "Value: 3.14"

十六进制格式化:

(format "Hex: %x" 255) ; 返回 "Hex: ff"

对齐和填充:

(format "%-10s%s" "Left" "Right") ; 返回 "Left      Right"

引号转义:

(format "He said, \"%s\"" "Hello") ; 返回 "He said, "Hello""

多个参数:

(format "%s: %d" "Answer" 42) ; 返回 "Answer: 42"