2015-06-02 35 views
4

讀取CL中csv文件的最快方法是: 1)第一行中的所有字段都進入一個名爲列名稱的數組中 2)第一個字段以下所有行中的每一行進入另一個 數組,稱爲行名稱 3)所有其他字段進入另一個數組,稱爲值 ?Common Lisp中的快速CSV讀取

我的文件中有如下形式,只是多了很多列和行:

"";"ES1 Index";"VG1 Index";"TY1 Comdty";"RX1 Comdty";"GC1 Comdty" 
"1999-01-04";1391.12;3034.53;66.515625;86.2;441.39 
"1999-01-05";1404.86;3072.41;66.3125;86.17;440.63 
"1999-01-06";1435.12;3156.59;66.4375;86.32;441.7 
"1999-01-07";1432.32;3106.08;66.25;86.22;447.67 

而且我想結果是:

#("1999-01-04" "1999-01-05" "1999-01-06" "1999-01-07") 
#("" "ES1 Index" "VG1 Index" "TY1 Comdty" "RX1 Comdty" "GC1 Comdty") 
#(1391.12 3034.53 66.515625 86.2 441.39 1404.86 3072.41 66.3125 86.17 440.63 
    1435.12 3156.59 66.4375 86.32 441.7 1432.32 3106.08 66.25 86.22 447.67) 

你知道一些CL庫那這樣做了嗎? 是否有任何關於I/O性能的普遍問題,或許是編譯器特有的,我應該知道?

這裏是這樣,我現在這樣做:

(with-open-file (stream "my-file.csv" :direction :input) 
    (let* ((header (read-line stream nil)) 
      (columns-list (mapcar #'read-from-string 
           (cl-ppcre:split ";" header))) 
      (number-of-columns (length columns-list)) 
      (column-names (make-array number-of-columns 
            :initial-contents columns-list)) 
      (rownames (make-array 1 :adjustable t :fill-pointer 0)) 
      (values (make-array 1 :adjustable t :fill-pointer 0))) 
(set-syntax-from-char #\; #\) 
(loop 
    :for reader = (read stream nil stream) 
    :until (eq reader stream) 
    :do (progn (vector-push-extend reader row-names) 
      (loop 
       :for count :from 2 :upto number-of-columns 
       :do (vector-push-extend (read stream nil) 
            values))) 
    :finally (return (values row-names 
         column-names 
         values))))) 

注:我不會用設置語法從炭在實際的代碼,我使用它只是爲了這個例子的目的。

+1

其他答案給你最快的便攜方式。最快的不可移植的方式是儘量減少複製(最慢的部分)。複製是在分割字符串時發生的(例如,基於引號或在讀線中的新行分割)。如果您的實現可以被確信實現並支持某種C FFI,它允許您在某個只存儲指針和長度的cl對象中引用c樣式數組,那麼您可以將csv文件進行mmap映射並解析它ram,通過指向內存中的文件來建立你的字符串。這不需要複製,但不會允許轉義引號。 –

回答

1

我懷疑I/O是這裏最慢的部分。如果您使用READ-SEQUENCE而不是重複呼叫READ-LINE,則可能會獲得更快的I/O。所以,你的代碼可能是這個樣子:

(with-open-file (s "my-file.csv") 
    (let* ((len (file-length s)) 
     (data (make-array len))) 
    (read-sequence data s) 
    data)) 

然後由新行分割data並添加您的邏輯。

無論是否有幫助,它都有助於您剖析代碼,例如,與:sb-sprof,看看大部分時間都花在哪裏。

1

要閱讀csv文件,我發現非常有用和快速的cl-csv包(https://github.com/AccelerationNet/cl-csv)。例如,爲了解決您的問題,下面的代碼可用於:

(let ((data (cl-csv:read-csv #P"my-file.csv" :separator #\;))) 
    (values (apply #'vector (first data)) 
      (apply #'vector (rest (mapcar #'first data))) 
      (apply #'vector 
      (mapcar #'read-from-string (loop :for row :in (rest data) 
               :append (rest row)))))) 

cl-csv:read-csv返回一個列表contaning,對於每一行,這是單元格的內容字符串列表。

+0

感謝您的輸入。我試過了。不幸的是,它需要我寫代碼的1.5倍。我在這裏尋找表演。 –

+0

你使用什麼CL實現? – Renzo

+0

我正在使用SBCL,但我也打算嘗試其他實現。這實際上與我在帖子中提到的問題有關:I/O性能可能存在一些常見問題,或許是編譯器特定的問題,我應該知道這些問題? –