读取字节忽略 SBCL 灰色流中的 eof-error-p
read-byte ignores eof-error-p inside SBCL gray stream
所以,我专门研究 read-byte
在 SBCL 上使用灰色流的方法。我 运行 出现了一种奇怪的行为,其中 eof-error-p
参数似乎被忽略了。可能是我在我的代码中遗漏了一些微不足道的错误,但我已经看了十几遍了,就是没看到。
假设有一个文件test.txt
只有一个字节,比方说
echo -n 7 > test.txt
我希望下面的代码能够 return :eof
(defclass binary-input-stream (fundamental-binary-input-stream)
((stream :initarg :stream :reader stream-of)))
(defmethod stream-read-byte ((stream binary-input-stream))
(format t "~a~%" "It's me, all right")
(read-byte (stream-of stream) nil :eof))
(defun make-binary-input-stream (stream)
(make-instance 'binary-input-stream :stream stream))
(with-open-file (in "test.txt" :element-type '(unsigned-byte 8))
(setq in (make-binary-input-stream in))
(read-byte in)
(read-byte in))
但是,SBCL 抛出 END-OF-FILE
异常。这里发生了什么?
我可以重现这个例子,调试器显示(在 Slime 下):
Backtrace:
0: (READ-BYTE #<BINARY-INPUT-STREAM {1039A8D933}> T NIL)
1: ((LAMBDA ()))
...
通过将光标移动到第0帧(即read-byte
)并按v,编辑器在[=14=中显示read-byte
的定义](我从源代码构建它):
(defun read-byte (stream &optional (eof-error-p t) eof-value)
(declare (explicit-check))
(if (ansi-stream-p stream)
(ansi-stream-read-byte stream eof-error-p eof-value nil)
;; must be Gray streams FUNDAMENTAL-STREAM
(let ((byte (stream-read-byte stream)))
(if (eq byte :eof)
(eof-or-lose stream eof-error-p eof-value) ;; <<<< CURSOR HERE
(the integer byte)))))
事实证明,:eof
已被 SBCL 用于指示行的 enf,并且由于对 read-byte
的顶级调用不会忽略错误,这会导致发出信号.
用另一个关键字替换 :eof
关键字,比如 :my-eof
,也不好,因为 returned 值不是字节。但是如果你 return -1
,测试通过(源流是无符号字节流,但是你的包装器可能 return -1 而没有错误)。
这是对 coredump 答案的补充。特别是我认为您的代码的行为是正确的:SBCL 在其灰色流的实现中做了正确的事情,它确实应该在这里发出异常信号。
在您的代码中,调用模式是:
read-byte
(没有可选参数)在您的 binary-input-stream
上调用 stream-read-byte
同一流;
- 你在
stream-read-byte
上的方法直接调用你包装的流上的 read-byte
,要求没有错误和 :eof
的 EOF 上的 return,并且 returns 该调用的值,无需进一步检查;
- 这大概会通过包装流的
stream-read-byte
方法,但我们不必担心。
那么,这应该怎么办?好吧,我不确定 Gray 流的最终文档的正确位置在哪里,但是 here is something which may be close to it.
在该文档中,stream-read-byte
被定义为:
STREAM-READ-BYTE stream [Generic Function]
Used by READ-BYTE; returns either an integer, or the symbol :EOF if the
stream is at end-of-file.
read-byte
则定义为:
(defun READ-BYTE (binary-input-stream &optional (eof-errorp t) eof-value)
(check-for-eof (stream-read-byte binary-input-stream)
binary-input-stream eof-errorp eof-value))
最后check-for-eof
定义为:
(defun check-for-eof (value stream eof-errorp eof-value)
(if (eq value :eof)
(report-eof stream eof-errorp eof-value)
value))
(我认为最后两个定义确实意味着 'the implementation needs to do something whose behaviour is equivalent to this',特别是在流是灰色流的情况下需要这样做。)
所以 stream-read-byte
上的任何方法都不能 return :eof
除非流位于文件末尾,特别是这样做将导致发出异常信号(或者将导致 report-eof
被调用,无论如何,它可能会或可能不会发出异常信号)。而:eof
是唯一的特殊值,stream-read-byte
可以return。
好吧,你在 stream-read-byte
上的方法确实 return :eof
指示文件结束,小心地抑制了内部调用 yo read-byte
否则会出现的异常信号,所以这是一个很好的方法。
但是随后 outer 对 read-byte
的调用,如上定义,会看到此 EOF 值并尽职地为您引发异常。事实上,这正是您所要求的,因为您没有要求抑制这些调用中的异常。
如果您不想要异常,您需要确保对 read-byte
的外部调用要求异常不会发生,例如:
(with-open-file (in "test.txt" :element-type '(unsigned-byte 8))
(with-open-stream (bin (make-binary-input-stream in))
(values (read-byte bin nil ':eof)
(read-byte bin nil ':eof))))
对于单字节文件,这应该 return 文件中的字节和 :eof
.
所以,我专门研究 read-byte
在 SBCL 上使用灰色流的方法。我 运行 出现了一种奇怪的行为,其中 eof-error-p
参数似乎被忽略了。可能是我在我的代码中遗漏了一些微不足道的错误,但我已经看了十几遍了,就是没看到。
假设有一个文件test.txt
只有一个字节,比方说
echo -n 7 > test.txt
我希望下面的代码能够 return :eof
(defclass binary-input-stream (fundamental-binary-input-stream)
((stream :initarg :stream :reader stream-of)))
(defmethod stream-read-byte ((stream binary-input-stream))
(format t "~a~%" "It's me, all right")
(read-byte (stream-of stream) nil :eof))
(defun make-binary-input-stream (stream)
(make-instance 'binary-input-stream :stream stream))
(with-open-file (in "test.txt" :element-type '(unsigned-byte 8))
(setq in (make-binary-input-stream in))
(read-byte in)
(read-byte in))
但是,SBCL 抛出 END-OF-FILE
异常。这里发生了什么?
我可以重现这个例子,调试器显示(在 Slime 下):
Backtrace:
0: (READ-BYTE #<BINARY-INPUT-STREAM {1039A8D933}> T NIL)
1: ((LAMBDA ()))
...
通过将光标移动到第0帧(即read-byte
)并按v,编辑器在[=14=中显示read-byte
的定义](我从源代码构建它):
(defun read-byte (stream &optional (eof-error-p t) eof-value)
(declare (explicit-check))
(if (ansi-stream-p stream)
(ansi-stream-read-byte stream eof-error-p eof-value nil)
;; must be Gray streams FUNDAMENTAL-STREAM
(let ((byte (stream-read-byte stream)))
(if (eq byte :eof)
(eof-or-lose stream eof-error-p eof-value) ;; <<<< CURSOR HERE
(the integer byte)))))
事实证明,:eof
已被 SBCL 用于指示行的 enf,并且由于对 read-byte
的顶级调用不会忽略错误,这会导致发出信号.
用另一个关键字替换 :eof
关键字,比如 :my-eof
,也不好,因为 returned 值不是字节。但是如果你 return -1
,测试通过(源流是无符号字节流,但是你的包装器可能 return -1 而没有错误)。
这是对 coredump 答案的补充。特别是我认为您的代码的行为是正确的:SBCL 在其灰色流的实现中做了正确的事情,它确实应该在这里发出异常信号。
在您的代码中,调用模式是:
read-byte
(没有可选参数)在您的binary-input-stream
上调用stream-read-byte
同一流;- 你在
stream-read-byte
上的方法直接调用你包装的流上的read-byte
,要求没有错误和:eof
的 EOF 上的 return,并且 returns 该调用的值,无需进一步检查; - 这大概会通过包装流的
stream-read-byte
方法,但我们不必担心。
那么,这应该怎么办?好吧,我不确定 Gray 流的最终文档的正确位置在哪里,但是 here is something which may be close to it.
在该文档中,stream-read-byte
被定义为:
STREAM-READ-BYTE stream [Generic Function]
Used by READ-BYTE; returns either an integer, or the symbol :EOF if the
stream is at end-of-file.
read-byte
则定义为:
(defun READ-BYTE (binary-input-stream &optional (eof-errorp t) eof-value)
(check-for-eof (stream-read-byte binary-input-stream)
binary-input-stream eof-errorp eof-value))
最后check-for-eof
定义为:
(defun check-for-eof (value stream eof-errorp eof-value)
(if (eq value :eof)
(report-eof stream eof-errorp eof-value)
value))
(我认为最后两个定义确实意味着 'the implementation needs to do something whose behaviour is equivalent to this',特别是在流是灰色流的情况下需要这样做。)
所以 stream-read-byte
上的任何方法都不能 return :eof
除非流位于文件末尾,特别是这样做将导致发出异常信号(或者将导致 report-eof
被调用,无论如何,它可能会或可能不会发出异常信号)。而:eof
是唯一的特殊值,stream-read-byte
可以return。
好吧,你在 stream-read-byte
上的方法确实 return :eof
指示文件结束,小心地抑制了内部调用 yo read-byte
否则会出现的异常信号,所以这是一个很好的方法。
但是随后 outer 对 read-byte
的调用,如上定义,会看到此 EOF 值并尽职地为您引发异常。事实上,这正是您所要求的,因为您没有要求抑制这些调用中的异常。
如果您不想要异常,您需要确保对 read-byte
的外部调用要求异常不会发生,例如:
(with-open-file (in "test.txt" :element-type '(unsigned-byte 8))
(with-open-stream (bin (make-binary-input-stream in))
(values (read-byte bin nil ':eof)
(read-byte bin nil ':eof))))
对于单字节文件,这应该 return 文件中的字节和 :eof
.