3

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!

4
  • 1
    You CAN'T create local variables with SETQ. Where did you get this use of SETQ from? Commented Feb 8, 2017 at 7:52
  • @RainerJoswig: why is that? That's what my professor uses in the course. Commented Feb 8, 2017 at 12:51
  • 1
    Because SETQ is defined to set existing variables. It does not introduce new variables, especially not local variables. Local variables are introduced for example by LET and LET*. If your professor is using SETQ like in your example, you might inform him/her with great care, that this is wrong. If he/she has questions they are welcome to Stackoverflow and we will explain it... Commented Feb 8, 2017 at 13:12
  • 1
    @RainerJoswig: I think this pervasive attempted use of assignment operators for binding is likely Python braindamage. The confusion of assignment and binding might be Python's single worst feature. Commented Feb 8, 2017 at 16:49

1 Answer 1

9

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)

E.g., 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 an error.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.