带有quicklisp和cl-csv的Common Lisp:如何从大型csv返回单行

时间:2018-04-16 12:58:39

标签: csv common-lisp quicklisp

我正致力于实现一个神经网络来处理CSV中的MNIST数据集,而不是使用图像。我使用Common Lisp和Quicklisp,以及cl-csv实用程序进行CSV解析。使用cl-csv,如何从CSV返回单行?使用(cl-csv:read-csv-row #P"file.csv")会返回第1行。尝试(cl-csv:read-csv-row #P"file.csv" 5)会产生:*** - CL-CSV:READ-CSV-ROW: keyword arguments in (#P"test3.csv") should occur pairwise。 cl-csv可以返回单个指定的行,如果是,那么如何将行号写为参数?

2 个答案:

答案 0 :(得分:1)

一个名为read-…的函数通常被认为是从流中读取。这涉及改变流的状态,以便下一次读取交互从前一个左侧关闭开始。一个常见的习语是循环执行此操作。

似乎cl-csv希望read-csv-row的用户将end-of-file作为信号处理,所以:

(with-open-file (csv-in-stream csv-pathname)
  (handler-case
      (loop :for csv-line := (read-csv-row csv-in-stream)
            :do (process-somehow csv-line))
    (end-of-file () (whatever)))

如果您想获得一个特定的行:

(with-open-file (csv-in-stream csv-pathname)
  (handler-case
      (loop :repeat (1- n)
            :do (read-csv-row csv-in-stream) ; skip skip …
            :finally (return (read-csv-row csv-in-stream)))
    (end-of-file () (oupsie-file-too-short)))

您经常需要使用提供的便利包装器之一:

(do-csv (row csv-pathname)
  (process-somehow row))

或使用iterate

(iter
  (for row in-csv csv-pathname)
  (process-somehow row))

我必须承认,我已经非常喜欢替代库fare-csv

答案 1 :(得分:0)

<强>解决方案

(ql:quickload 'cl-csv) ; load  the cl-csv package

(defun nth-csv-row (csv-path n &rest read-csv-row-parameters)
  "Return nth line of a csv file, parsed."
  (with-open-file (stream csv-path)
    (loop for x from 1 below n
          do (cl-csv:read-csv-row stream))
    (apply #'cl-csv:read-csv-row stream read-csv-row-parameters)))

;; your example executed using the new function:
(nth-csv-row #P"file.csv" 5)

致@Svante,他指出了我犯的逻辑错误。 (最初,我使用do (read-line stream)跳过这些行。但由于new-line字符可以在csv单元格内,我必须使用cl-csv:read-csv-row来正确解析单元格包含的情况new-line s。谢谢@Svante!

错误的(!)旧解决方案(仅限教育用途)

(ql:quickload 'cl-csv) ; load  the cl-csv package

;; a more general function returning the nth line of a file
(defun nth-line (file-path n)
  (with-open-file (stream file-path)
    (loop for x from 1 to (1- n) 
          do (read-line stream))
    (read-line stream)))

;; wrap it with a csv parsing function
(defun nth-csv-line (csv-path n &rest read-csv-row-parameters)
  "Return nth line of a csv file, parsed."
  (apply #'cl-csv:read-csv-row (nth-line csv-path n) read-csv-row-parameters))

;; your example executed using the new function:
(nth-csv-line #P"file.csv" 5)

(如果csv单元格包含换行符,则无法正确解析!) - (read-line)不会检查new-line字符是在单元格内还是在单元格外部。“ p>

无论如何 - 现在接下来,我之前评论过的(仍然有效):

自:

  

[功能] read-csv-row(stream-or-string&amp; key(分隔符    separator )(quote quote )(escape quote-escape )&amp; aux current state line llen c elen)=&gt; 结果

     

按数据行读取CSV(由于引用的换行符可能更多   来自流的一行)   (https://github.com/AccelerationNet/cl-csv/blob/master/DOCUMENTATION.md#read-csv-row

由于&rest read-csv-row-parameters将所有其他参数传递给cl-csv:read-csv-row函数(与R ...完全相同), nth-csv-line具有cl-csv:read-csv-row功能的全部功能。因此,

此解决方案不仅可以使用逗号分隔,还可以使用任何其他分隔符分隔的数据

示例:

考虑"~/test.csv"内容:

abc def klm
1   2   3
A   B   C

(注意:这是制表符分隔文件而不是逗号分隔文件)

解析第二行:

(nth-csv-row "~/test.csv" 2 :separator #\TAB) ; instead of comma

;; returns - correctly parsed: ;; ("1" "2" "3")

附录(正确安装quicklisp,运行这些代码段......)

如果有人读到这篇文章,那么新手想要尝试并且没有quicklisp工作(我必须重新弄清楚它 - 所以也许可以节省你的时间):

;; ;; If quicklisp is not installed, do on terminal:
;; $ wget https://beta.quicklisp.org/quicklisp.lisp
;; ;; Then in your lisp interpreter:
;; (quicklisp-quickstart:install)
;; ;; following instructions of quickslisp do
;; (load "~/quicklisp/setup.lisp") ; or: path/to/your/quicklisp/setup.lisp

;; With installed quicklisp, you can from now on install and load 
;; any quicklisp-able package by:
(ql:quickload 'cl-csv) ; install cl-csv using quicklisp