【问题标题】:How does read-line work in Lisp when reaching eof?到达 eof 时,Lisp 中的 read-line 是如何工作的?
【发布时间】:2017-06-25 11:17:18
【问题描述】:

上下文: 我有一个名为 fr.txt 的文本文件,其中包含 3 列文本:

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

我想创建一个函数来读取第一列(最终也是第三列)并将其写入另一个名为 alphabet_code.txt 的文本文件中。

到目前为止我有这个功能:

(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)
)

我的问题:

  1. 我不太明白read-line的参数是怎么起作用的。我读过this doc,但对我来说仍然很模糊。如果有人有非常简单的例子,那会有所帮助。

  2. 使用当前代码,我收到此错误:*** - read: input stream #<input string-input-stream> has reached its end,即使我将 (read-line source nil nil) 中的 nil nil 更改为其他值。

感谢您的宝贵时间!

【问题讨论】:

  • 您不能使用 SETQ 创建局部变量。你从哪里得到 SETQ 的这种用法?
  • @RainerJoswig:为什么?这就是我的教授在课程中使用的。
  • 因为 SETQ 被定义为设置现有变量。它不引入新变量,尤其是局部变量。局部变量例如由 LET 和 LET* 引入。如果您的教授在您的示例中使用 SETQ,您可能会非常小心地告知他/她,这是错误的。如果他/她有任何问题,欢迎来到 Stackoverflow,我们会解释...
  • @RainerJoswig:我认为这种普遍使用赋值运算符进行绑定的尝试可能是 Python 的脑损伤。赋值和绑定的混淆可能是 Python 最糟糕的一个特性。

标签: file-io lisp common-lisp


【解决方案1】:

您的问题

read-line 可选参数

read-line 接受 3 个可选参数:

  1. eof-error-p:在 EOF 上做什么(默认:错误)
  2. eof-value: 当你看到 EOF 时返回什么而不是错误
  3. recursive-p:你是从你的 print-object 方法调用它吗(暂时忘记这个)

例如,当 stream 处于 EOF 时,

  • (read-line stream) 将发出 end-of-file 错误的信号
  • (read-line stream nil) 将返回 nil
  • (read-line stream nil 42) 将返回 42

请注意,(read-line stream nil)(read-line stream nil nil) 相同,但人们通常仍显式传递第二个可选参数。 eof-valuenil 适合 read-line,因为 nil 不是字符串,read-line 只返回字符串。

另请注意,在 read 的情况下,第二个可选参数通常是 stream 本身:(read stream nil stream)。挺方便的。

错误

您收到来自read-from-string 的错误,而不是read-line,因为显然您的文件中有一个空行。

我知道是因为错误提到了string-input-stream,而不是file-stream

您的代码

您的代码在功能上是正确的,但在风格上却非常错误。

  1. 您应该尽可能使用with-open-file
  2. 您不应该在代码中使用print,这是一个奇怪的遗留函数,主要用于交互使用。
  3. 您不能使用 setq 创建局部变量 - 使用 let 或其他等效形式(在这种情况下,您永远不需要 let!:-)

这是我将如何重写你的函数:

(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")

查看文档:

  1. loopfor/aswhiledo
  2. write, write-char
  3. parse-integer

或者,我可以使用相应的loop conditional,而不是(when num ...)

另外,我可以写成(format code "~D~%" num),而不是write+write-char

请注意,我确实传递与默认值相同的with-open-stream 参数。 默认设置是一成不变的,您编写的代码和用户必须阅读的代码越少,出错的可能性就越小。

【讨论】:

    猜你喜欢
    • 2011-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-24
    • 2013-12-07
    • 2012-02-01
    • 1970-01-01
    • 2022-12-13
    相关资源
    最近更新 更多