大部分用于读取文本的 Lisp 函数都会接受一个 输入流(input stream) 作为参数。 输入流指定从何处、以何种方式获取待读取的字符。 输入流可以是以下类型:
从缓冲区 buffer 中读取字符,起始位置为光标后一位。 随着字符被读取,光标会向后移动。
从标记 marker 所在的缓冲区读取字符,起始位置为标记后一位。 随着字符被读取,标记位置会向后移动。 当流是标记时,缓冲区中的光标位置不产生影响。
从字符串 string 中获取输入字符,从字符串第一个字符开始,按需读取。
输入字符由函数 function 生成,该函数必须支持两种调用方式:
t ¶t 作为流表示从迷你缓冲区读取输入。
实际上,迷你缓冲区会被激活一次,用户输入的文本会被构造成字符串并作为输入流使用。
如果 Emacs 以批处理模式运行(see Batch Mode),则使用标准输入而非迷你缓冲区。
例如:
(message "%s" (read t))
在批处理模式下会从标准输入读取一个 Lisp 表达式,并将结果打印到标准输出。
nil ¶nil 作为输入流表示使用变量 standard-input 的值;
该值是 默认输入流(default input stream),必须是一个非nil 的输入流。
符号作为输入流等价于使用该符号的函数定义(如果存在)。
下面是从缓冲区流中读取的例子,展示读取前后光标的位置:
---------- Buffer: foo ---------- This∗ is the contents of foo. ---------- Buffer: foo ----------
(read (get-buffer "foo"))
⇒ is
(read (get-buffer "foo"))
⇒ the
---------- Buffer: foo ---------- This is the∗ contents of foo. ---------- Buffer: foo ----------
注意第一次读取会跳过一个空格。读取会自动跳过有效文本前的任意空白。
下面是从标记流读取的例子,标记初始位于缓冲区开头。
读取到的值是符号 This。
---------- Buffer: foo ---------- This is the contents of foo. ---------- Buffer: foo ----------
(setq m (set-marker (make-marker) 1 (get-buffer "foo")))
⇒ #<marker at 1 in foo>
(read m)
⇒ This
m
⇒ #<marker at 5 in foo> ;; Before the first space.
下面从字符串内容中读取:
(read "(When in) the course")
⇒ (When in)
下面的例子从迷你缓冲区读取。提示语为:‘Lisp expression: ’。
(使用流 t 读取时总是使用该提示。)
提示后的内容为用户输入。
(read t)
⇒ 23
---------- Buffer: Minibuffer ----------
Lisp expression: 23 RET
---------- Buffer: Minibuffer ----------
最后是一个函数流的例子,函数名为 useless-stream。
在使用该流之前,我们先将变量 useless-list 初始化为一个字符列表。
之后每次调用 useless-stream 会从列表中取下一个字符,
或通过将字符添加到列表头部来回退字符。
(setq useless-list (append "XY()" nil))
⇒ (88 89 40 41)
(defun useless-stream (&optional unread)
(if unread
(setq useless-list (cons unread useless-list))
(prog1 (car useless-list)
(setq useless-list (cdr useless-list)))))
⇒ useless-stream
现在用构造出的流进行读取:
(read 'useless-stream)
⇒ XY
useless-list
⇒ (40 41)
注意左右括号仍然留在列表中。
Lisp 读取器遇到左括号后判定输入结束,并将其回退。
此时再次从该流读取会读到 ‘()’ 并返回 nil。