简体   繁体   中英

How does read-line work in Lisp when reaching eof?

Context: I have a text file called fr.txt with 3 columns of text in it:

65  A   #\A
97  a   #\a
192     À   #\latin_capital_letter_a_with_grave
224     à   #\latin_small_letter_a_with_grave
etc...

I want to create a function to read the first (and eventually the third one too) column and write it into another text file called alphabet_code.txt .

So far I have this function:

(defun alphabets()
   (setq source (open "fr.txt" :direction :input :if-does-not-exist :error))
   (setq code (open "alphabet_code.txt" :direction :output :if-does-not-exist :create :if-exists :supersede))
   (loop
      (setq ligne (read-line source nil nil))
      (cond
         ((equal ligne nil) (return))
         (t (print (read-from-string ligne) code))
      )
   )
   (close code)
   (close source)
)

My problems:

  1. I don't really understand how the parameters of read-line function. I have read this doc , but it's still very obscure to me. If someone would have very simple examples, that would help.

  2. With the current code, I get this error: *** - read: input stream #<input string-input-stream> has reached its end even if I change the nil nil in (read-line source nil nil) to other values.

Thanks for your time!

Your questions

read-line optional arguments

read-line accepts 3 optional arguments:

  1. eof-error-p : what to do on EOF (default: error)
  2. eof-value : what to return instead of the error when you see EOF
  3. recursive-p : are you calling it from your print-object method (forget about this for now)

Eg, when the stream is at EOF,

  • (read-line stream) will signal the end-of-file error
  • (read-line stream nil) will return nil
  • (read-line stream nil 42) will return 42 .

Note that (read-line stream nil) is the same as (read-line stream nil nil) but people usually still pass the second optional argument explicitly. eof-value of nil is fine for read-line because nil is not a string and read-line only returns strings.

Note also that in case of read the second optional argument is, traditionally, the stream itself: (read stream nil stream) . It's quite convenient.

Error

You are getting the error from read-from-string , not read-line , because, apparently, you have an empty line in your file.

I know that because the error mentions string-input-stream , not file-stream .

Your code

Your code is correct functionally, but very wrong stylistically.

  1. You should use with-open-file whenever possible.
  2. You should not use print in code, it's a weird legacy function mostly for interactive use.
  3. You can't create local variables with setq - use let or other equivalent forms (in this case, you never need let ! :-)

Here is how I would re-write your function:

(defun alphabets (input-file output-file)
  (with-open-stream (source input-file)
    (with-open-stream (code output-file :direction :output :if-exists :supersede)
      (loop for line = (read-line source nil nil)
          as num = (parse-integer line :junk-allowed t)
        while line do
          (when num
            (write num :stream code)
            (write-char #\Newline code))))))
(alphabets "fr.txt" "alphabet_code.txt")

See the docs:

  1. loop : for/as , while , do
  2. write , write-char
  3. parse-integer

Alternatively, instead of (when num ...) I could have use the corresponding loop conditional .

Also, instead of write + write-char I could have written (format code "~D~%" num) .

Note that I do not pass those of your with-open-stream arguments that are identical to the defaults. The defaults are set in stone, and the less code you have to write and your user has to read, the less is the chance of error.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM