Elisp 基本数据类型:字符和字符串
字符
构成字符串的字符其实就是一个整数,目前字符串中的字符被限制在 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"