> For the complete documentation index, see [llms.txt](https://emacs-lisp.ivory.cafe/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://emacs-lisp.ivory.cafe/du-qu-he-da-yin-lisp-dui-xiang/19.2-shu-ru-liu.md).

# 19.2 输入流

绝大多数用于读取文本的 Lisp 函数都可以接受一个 input stream 作为参数。输入流指明了，从什么地方，以及如何读取文本中字符。这里列举了一下可以作为输入流的对象类型：

buffer（缓冲区）

输入字符来自于缓冲区 buffer，从位点开始。其中位点处于待读取字符的前面一格。

marker（标记）

输入字符来自于标记 marker 所在缓冲区，从标记开始。曲中标记处于待读取字符的前面一格。将标记作为流并不会对标记的值产生副作用。

string（字符串）

输入字符来自于字符串 string，从字符串的第一个字符开始，并尽可能贪婪的读取字符。

function（函数）

输入字符由函数 function生产，这个函数必须满足以下两点：

* 该函数被无参调用时，应当返回下一个待读取的字符。
* 该函数被单参调用时（通常参数为单个字符），函数应该保存参数，并将其整理，用于下一次的调用。这个行为称为预读取（unreading）字符；预读取通常发生在这种情况：Lisp 读取器 多次读取单个字符，并想将其重新放回原流。这种情况下，函数返回值将不那么重要。

t

t 用于表示从 minibuffer 中读取。实际上，minibuffer 仅会被调用一次，将用户的输入处理为一个字符串，再将该字符串作为输入流。如果 Emacs 在 批处理模式（查阅 批处理模型） 下运行，那么将会使用标准输入，而不是minibuffer。例如在批处理模式下，

```
(message "%s" (read t))
```

将会从标准输入读取 Lisp 表达式，然后将结果打印到标准输出。

nil

nil 表示使用变量 standart-input 的值作为输入流；这个值通常是默认的输入流，而且必须为 非nil 输入流。

symbol

当一个符号作为输入流时，Emacs 会检查其函数定义，并将该定义作为输入流。

这里有个例子，展示了将缓冲区 buffer 作为输入流时，对位点的副作用：

```
---------- 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 ----------
```

请注意，第一次读取时，跳过了一个空格。读取会跳过关键文本前的适量空格。

这里有一个将标记 marker 作为输入流的例子，其读取到的值为一个符号 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)
```

下面这个例子，我们从 minibuffer 中读取内容。其中" Lisp expression："是提示语。（当你从流 t 中读取时，总是会有提示语）用户的输入紧跟在提示语。

```
(read t)
     ⇒ 23
---------- Buffer: Minibuffer ----------
Lisp expression: 23 RET
---------- Buffer: Minibuffer ----------
```

最后这个例子中，我们从将函数作为输入流。这个函数名称为 useless-stream。在读取前，我们需要初始化变量 useless-list。之后每一次对函数 useless-steam的调用，都会从该 变量 中获取下一个字符。

```
(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)
```

请注意，开闭括号仍在 list 中。Lisp 读取器会对开括号进行计数，以此判断输入何时结束，并停止读取。你可以尝试另一个例子，读取"()"将会返回nil。
