And*_*age 3 io sbcl common-lisp stream ccl
在Common Lisp HyperSpec页面上unread-char- 请参阅
此处 - 它说明了以下两点:
"unread-char旨在成为允许Lisp读者和其他解析器在输入流中执行单字符预测的有效机制."
"在同一个流上连续两次调用unread-char是错误的,而没有在该流上调用read-char(或其他隐式读取字符的输入操作)."
我正在研究如何为我正计划编写的解析器添加对CL流的多字符前瞻支持,并且为了确认上述内容,我运行了以下代码:
(defun unread-char-test (data)
(with-input-from-string (stream data)
(let ((stack nil))
(loop
for c = (read-char stream nil)
while c
do (push c stack))
(loop
for c = (pop stack)
while c
do (unread-char c stream)))
(coerce
(loop
for c = (read-char stream nil)
while c
collect c)
'string)))
(unread-char-test "hello")
==> "hello"
Run Code Online (Sandbox Code Playgroud)
它没有抛出错误(在SBCL或CCL上,我还没有在其他实现上测试它)但我没有看到可能有任何读取操作(隐式或显式)发生在流之间连续打电话给unread-char.
这种行为对于多字符前瞻来说是好消息,只要它是一致的,但为什么不抛出错误?
为了回应用户jkiiski的评论,我做了一些挖掘.我定义了一个类似于上面的函数,但它将流作为参数(为了更容易重用):
(defun unread-char-test (stream)
(let ((stack nil))
(loop
for c = (read-char stream nil)
while c
do (push c stack))
(loop
for c = (pop stack)
while c
do (unread-char c stream)))
(coerce
(loop
for c = (read-char stream nil)
while c
collect c)
'string))
Run Code Online (Sandbox Code Playgroud)
然后我在第二个REPL中运行以下内容:
(defun create-server (port)
(usocket:with-socket-listener (listener "127.0.0.1" port)
(usocket:with-server-socket (connection (usocket:socket-accept listener))
(let ((stream (usocket:socket-stream connection)))
(print "hello" stream)))))
(create-server 4000)
Run Code Online (Sandbox Code Playgroud)
以及第一个REPL中的以下内容:
(defun create-client (port)
(usocket:with-client-socket (connection stream "127.0.0.1" port)
(unread-char-test stream)))
(create-client 4000)
Run Code Online (Sandbox Code Playgroud)
它确实抛出了我预期的错误:
Two UNREAD-CHARs without intervening READ-CHAR on #<BASIC-TCP-STREAM ISO-8859-1 (SOCKET/4) #x302001813E2D>
[Condition of type SIMPLE-ERROR]
Run Code Online (Sandbox Code Playgroud)
这表明jkiiski的假设是正确的.从文本文件中读取输入时也会观察到原始行为,如下所示:
(with-open-file (stream "test.txt" :direction :output)
(princ "hello" stream))
(with-open-file (stream "test.txt")
(unread-char-test stream)))
==> "hello"
Run Code Online (Sandbox Code Playgroud)
我想,在处理本地文件I/O时,实现会将大块文件read-char读入内存,然后从缓冲区中读取.如果正确,这也支持这样的假设:当从内容在内存中的流中取消读取时,典型实现不会抛出规范中描述的错误.