Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Common Lisp output file streams SBCL

I am on SBCL on debian.

For some reason if I use this:

(with-open-file (output (open #p"file.txt"
               :direction :output
               :if-exists :overwrite))
   (format output "test")))

Where file.txt is a plain text file.

I get the error

#<SB-SYS:FD-STREAM for "file /home/me/file.txt" {1004A90813}> is not
a character output stream.

Even using :element-type 'character doesn't save me. I haven't been able to get any output stream opened by any method. If I try to use write-bit it says that it isn't a binary output stream. No other write functions work either, such as write-sequence or write-line. They all return this error. How do I fix this?

like image 954
Spenser Truex Avatar asked Dec 04 '22 03:12

Spenser Truex


1 Answers

I've made the important points bold. The problem is actually more tricky then one might think:

Let's look at the form.

First mistake: it's not indented correctly. Let's indent:

(with-open-file (output (open #p"file.txt"
                              :direction :output
                              :if-exists :overwrite))
  (format output "test")))

Now we can see more mistakes. An additional parentheses

(with-open-file (output (open #p"file.txt"
                              :direction :output
                              :if-exists :overwrite))
  (format output "test")))  ; <- additional parenthesis

But more important:

(open #p"file.txt"
      :direction :output
      :if-exists :overwrite)

Above opens a file for writing output and returns a stream.

WITH-OPEN-FILE does also open a file. So you try to open the file TWICE, first for writing..

(with-open-file (output stream)
  (format output "test")))

Above opens a file for reading. You have opened the file twice: first for writing, then for reading.

Now you try to write with FORMAT to an input stream.

The slightly surprising part is this: both open and with-open-file can take a file stream as a file spec. If it gets a file stream as a file spec, then the associated pathname is used for the open operation.

So, as mentioned in another answer, this would be more correct:

(with-open-file (output #p"file.txt"
                        :direction :output
                        :if-exists :supersede)
  (format output "Hello"))

SBCL error message:

#<SB-SYS:FD-STREAM for "file /home/me/file.txt" {1004A90813}>
is not a character output stream.

The point of the error message here is not that the stream is not a character stream. It's not an output stream. The stream actually is a character input stream! Thus calling FORMAT using the stream won't work. Let's write an assert to verify this:

CL-USER 18 > (with-open-file (output (open #p"/tmp/file.txt"
                                           :direction :output
                                           :if-does-not-exist :create
                                           :if-exists :overwrite))
               (assert (output-stream-p output) (output)
                       "The stream ~a is not an output stream!"
                       output)
               (format output "test"))
Error: The stream #<STREAM::LATIN-1-FILE-STREAM /tmp/file.txt>
is not an output stream!

Your extra question: Why is the following form working?

(with-open-file (input (open #p"file.txt")) ...)

It just opens the file TWICE for reading.

like image 197
Rainer Joswig Avatar answered Jan 09 '23 13:01

Rainer Joswig