# 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。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://emacs-lisp.ivory.cafe/du-qu-he-da-yin-lisp-dui-xiang/19.2-shu-ru-liu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
