Archive for the ‘list2csv’ Tag

newLISP で CSV を扱う(解説編)。

 先日の “newLISP で CSV を扱う。” は如何だったでしょうか?
(defun は newlisp-utility.lsp に定義してあります。)

(define CRLF "\r\n")
(define regexCRLF "\r*\n")
(defun stringEx (x)
  (if (string? x) (string "\"" x "\"") (string x)))
(defun string-convert (str) 
  (if (catch (eval-string str) 'res) (if res res str) str))
(defun list2csv (lst)
  (let (csv "")
    (dolist (row lst)
      (extend csv (join (map stringEx row) ",") CRLF))))
(defun csv2list (csv)
  (let (lst '())
    (dolist (row (parse (if (ends-with csv regexCRLF 0) (chop csv) csv) regexCRLF 0))
      (push (map string-convert (parse row ",")) lst -1))
    (setf (lst -1 -1) (if (string? $it) (trim $it "\r") $it))
    lst))

 短いコードなので、再掲載。といいつつ、さりげなく変更しています(汗)。
 関数list2csv では組込join を使ってCSV 文字列を作り、関数csv2list では組込parse を使ってリストにしているのは、定石通り?
 もちろん、ポイントは、関数stringEx と string-convert です。
 関数stringEx は、組込string のように文字列に変換しますが、引数が文字列の時は、ダブルクォートで囲まれた文字列に変換します。
 一方、関数string-convert は、引数に組込eval-string を適用して、エラーなら文字列そのまま出力します。組込eval-string がエラーを起こさなければ、それは評価されたわけですから、評価された値を出します。関数list2csv で作成した CSV 文字列だけを扱うなら、

(defun string-convert (str) 
  (if (catch (eval-string str) 'res) res str))

 でも良かったのですが、最初のコードのように評価値が nil の場合も、文字列のまま出すようにしています。
 これは、CSV 文字列中で文字列に変換したい部分が必ずしもダブルクォートで囲まれているとは限らないからです。
 ここが、EXCEL 等の出力する CSV ファイルにも対応する時のポイントです(笑)。
 問題があるとすれば、

> (setq test (list2csv '(("test" nil true))))
"\"test\",nil,true\r\n"
> (csv2list test)
(("test" "nil" true))
> 

 という風に、データが nil の時も文字列になってしまうことくらい?(笑)
 ちなみに、EXCEL のブール値 TRUE と FALSE も、文字列になります。
 さて、先日の関数は、ファイルを対象にしていません。なぜなら、組込read-filewrite-file を使えば良いだけだからです。

> csv
"\"Planet name\",\"Equator diameter (earth)\",\"Mass (earth)\",\"Orbital radius (AU)\",\"Orbital period (years)\",\"Orbital Incline Angle\",\"Orbital Eccentricity\",\"Rotation (days)\",\"Moons\"\r\n\"Mercury\",0.382,0.06,0.387,0.241,7,0.206,58.6,0\r\n\"Venus\",0.949,0.82,0.72,0.615,3.39,0.0068,-243,0\r\n\"Earth\",1,1,1,1,0,0.0167,1,1\r\n\"Mars\",0.53,0.11,1.52,1.88,1.85,0.0934,1.03,2\r\n\"Jupiter\",11.2,318,5.2,11.86,1.31,0.0484,0.414,63\r\n\"Saturn\",9.41,95,9.54,29.46,2.48,0.0542,0.426,49\r\n\"Uranus\",3.98,14.6,19.22,84.01,0.77,0.0472,-0.718,27\r\n\"Neptune\",3.81,17.2,30.06,164.8,1.77,0.0086,0.671,13\r\n\"Pluto\",0.18,0.002,39.5,248.5,17.1,0.249,-6.5,3\r\n\"test\",FALSE,TRUE,nil,true\r\n"
> (write-file "test.csv" csv)
639
> (csv2list (read-file "test.csv"))
(("Planet name" "Equator diameter (earth)" "Mass (earth)" "Orbital radius (AU)" "Orbital period (years)" 
  "Orbital Incline Angle" "Orbital Eccentricity" "Rotation (days)" "Moons") 
 ("Mercury" 0.382 0.06 0.387 0.241 7 0.206 58.6 0) 
 ("Venus" 0.949 0.82 0.72 0.615 3.39 0.0068 -243 0) 
 ("Earth" 1 1 1 1 0 0.0167 1 1) 
 ("Mars" 0.53 0.11 1.52 1.88 1.85 0.0934 1.03 2) 
 ("Jupiter" 11.2 318 5.2 11.86 1.31 0.0484 0.414 63) 
 ("Saturn" 9.41 95 9.54 29.46 2.48 0.0542 0.426 49) 
 ("Uranus" 3.98 14.6 19.22 84.01 0.77 0.0472 -0.718 27) 
 ("Neptune" 3.81 17.2 30.06 164.8 1.77 0.0086 0.671 13) 
 ("Pluto" 0.18 0.002 39.5 248.5 17.1 0.249 -6.5 3) 
 ("test" "FALSE" "TRUE" "nil" true))
> 

 こんな感じです。
 また、Linux で使われている改行だけの文字列にも、

> (replace "\r" csv "")
"\"Planet name\",\"Equator diameter (earth)\",\"Mass (earth)\",\"Orbital radius (AU)\",\"Orbital period (years)\",\"Orbital Incline Angle\",\"Orbital Eccentricity\",\"Rotation (days)\",\"Moons\"\n\"Mercury\",0.382,0.06,0.387,0.241,7,0.206,58.6,0\n\"Venus\",0.949,0.82,0.72,0.615,3.39,0.0068,-243,0\n\"Earth\",1,1,1,1,0,0.0167,1,1\n\"Mars\",0.53,0.11,1.52,1.88,1.85,0.0934,1.03,2\n\"Jupiter\",11.2,318,5.2,11.86,1.31,0.0484,0.414,63\n\"Saturn\",9.41,95,9.54,29.46,2.48,0.0542,0.426,49\n\"Uranus\",3.98,14.6,19.22,84.01,0.77,0.0472,-0.718,27\n\"Neptune\",3.81,17.2,30.06,164.8,1.77,0.0086,0.671,13\n\"Pluto\",0.18,0.002,39.5,248.5,17.1,0.249,-6.5,3\n\"test\",nil,true,FALSE,TRUE\n"
> (csv2list csv)
(("Planet name" "Equator diameter (earth)" "Mass (earth)" "Orbital radius (AU)" "Orbital period (years)" 
  "Orbital Incline Angle" "Orbital Eccentricity" "Rotation (days)" "Moons") 
 ("Mercury" 0.382 0.06 0.387 0.241 7 0.206 58.6 0) 
 ("Venus" 0.949 0.82 0.72 0.615 3.39 0.0068 -243 0) 
 ("Earth" 1 1 1 1 0 0.0167 1 1) 
 ("Mars" 0.53 0.11 1.52 1.88 1.85 0.0934 1.03 2) 
 ("Jupiter" 11.2 318 5.2 11.86 1.31 0.0484 0.414 63) 
 ("Saturn" 9.41 95 9.54 29.46 2.48 0.0542 0.426 49) 
 ("Uranus" 3.98 14.6 19.22 84.01 0.77 0.0472 -0.718 27) 
 ("Neptune" 3.81 17.2 30.06 164.8 1.77 0.0086 0.671 13) 
 ("Pluto" 0.18 0.002 39.5 248.5 17.1 0.249 -6.5 3) 
 ("test" "nil" true "FALSE" "TRUE"))
> 

 対応しています。parse に正規表現を使っていますから。
 ということで、さりげなく直しているこの部分。やはり、正規表現は、鬼門?(汗)

 以上、如何でしょうか?

newLISP で CSV を扱う。

 newLISP で CSV(Comma-Separated Values)を扱うなら、ArtfulCodeModule:CSV があります。ArtfulCode の名にふさわしい洗練されたコードです。
 と、言いつつ、今回は、自作します。何故か?それは後ほど。

 まずは、スクリプトから、
(defun は newlisp-utility.lsp に定義してあります。)

(define CRLF "\r\n")
(define regexCRLF "\r*\n")
(defun stringEx (x)
  (if (string? x) (string "\"" x "\"") (string x)))
(defun list2csv (lst)
  (let (csv "")
    (dolist (row lst)
      (extend csv (join (map stringEx row) ",") CRLF))))
(defun string-convert (str) 
  (if (catch (eval-string str) 'res) (if res res str) str))
(defun csv2list (csv)
  (let (lst '())
    (dolist (row (parse (if (ends-with csv regexCRLF 0) (chop csv) csv) regexCRLF 0))
      (push (map string-convert (parse row ",")) lst -1))))

 関数名からわかるように、CSV文字列とリストを相互に変換しますが、これだけです(笑)。
 だから、自作。いえいえ。それは動作させてみれば判ります。
 次のデータは、 Introduction to newLISP に載っていた太陽系惑星のデータを使わせてもらっています。

(setq sol-sys '(("Planet name" "Equator diameter (earth)" "Mass (earth)"
"Orbital radius (AU)" "Orbital period (years)"
"Orbital Incline Angle" "Orbital Eccentricity"
"Rotation (days)" "Moons")
("Mercury" 0.382 0.06 0.387 0.241 7.00 0.206 58.6 0)
("Venus" 0.949 0.82 0.72 0.615 3.39 0.0068 -243 0)
("Earth" 1.00 1.00 1.00 1.00 0.00 0.0167 1.00 1)
("Mars" 0.53 0.11 1.52 1.88 1.85 0.0934 1.03 2)
("Jupiter" 11.2 318 5.20 11.86 1.31 0.0484 0.414 63)
("Saturn" 9.41 95 9.54 29.46 2.48 0.0542 0.426 49)
("Uranus" 3.98 14.6 19.22 84.01 0.77 0.0472 -0.718 27)
("Neptune" 3.81 17.2 30.06 164.8 1.77 0.0086 0.671 13)
("Pluto" 0.18 0.002 39.5 248.5 17.1 0.249 -6.5 3)))

 では動作を、

> (setq csv (list2csv sol-sys))
"\"Planet name\",\"Equator diameter (earth)\",\"Mass (earth)\",\"Orbital radius (AU)\",\"Orbital period (years)\",\"Orbital Incline Angle\",\"Orbital Eccentricity\",\"Rotation (days)\",\"Moons\"\r\n\"Mercury\",0.382,0.06,0.387,0.241,7,0.206,58.6,0\r\n\"Venus\",0.949,0.82,0.72,0.615,3.39,0.0068,-243,0\r\n\"Earth\",1,1,1,1,0,0.0167,1,1\r\n\"Mars\",0.53,0.11,1.52,1.88,1.85,0.0934,1.03,2\r\n\"Jupiter\",11.2,318,5.2,11.86,1.31,0.0484,0.414,63\r\n\"Saturn\",9.41,95,9.54,29.46,2.48,0.0542,0.426,49\r\n\"Uranus\",3.98,14.6,19.22,84.01,0.77,0.0472,-0.718,27\r\n\"Neptune\",3.81,17.2,30.06,164.8,1.77,0.0086,0.671,13\r\n\"Pluto\",0.18,0.002,39.5,248.5,17.1,0.249,-6.5,3\r\n"
> (begin (print csv) nil)
"Planet name","Equator diameter (earth)","Mass (earth)","Orbital radius (AU)","Orbital period (years)","Orbital Incline Angle","Orbital Eccentricity","Rotation (days)","Moons"
"Mercury",0.382,0.06,0.387,0.241,7,0.206,58.6,0
"Venus",0.949,0.82,0.72,0.615,3.39,0.0068,-243,0
"Earth",1,1,1,1,0,0.0167,1,1
"Mars",0.53,0.11,1.52,1.88,1.85,0.0934,1.03,2
"Jupiter",11.2,318,5.2,11.86,1.31,0.0484,0.414,63
"Saturn",9.41,95,9.54,29.46,2.48,0.0542,0.426,49
"Uranus",3.98,14.6,19.22,84.01,0.77,0.0472,-0.718,27
"Neptune",3.81,17.2,30.06,164.8,1.77,0.0086,0.671,13
"Pluto",0.18,0.002,39.5,248.5,17.1,0.249,-6.5,3
nil
> (csv2list csv)
(("Planet name" "Equator diameter (earth)" "Mass (earth)" "Orbital radius (AU)" "Orbital period (years)" 
  "Orbital Incline Angle" "Orbital Eccentricity" "Rotation (days)" "Moons") 
 ("Mercury" 0.382 0.06 0.387 0.241 7 0.206 58.6 0) 
 ("Venus" 0.949 0.82 0.72 0.615 3.39 0.0068 -243 0) 
 ("Earth" 1 1 1 1 0 0.0167 1 1) 
 ("Mars" 0.53 0.11 1.52 1.88 1.85 0.0934 1.03 2) 
 ("Jupiter" 11.2 318 5.2 11.86 1.31 0.0484 0.414 63) 
 ("Saturn" 9.41 95 9.54 29.46 2.48 0.0542 0.426 49) 
 ("Uranus" 3.98 14.6 19.22 84.01 0.77 0.0472 -0.718 27) 
 ("Neptune" 3.81 17.2 30.06 164.8 1.77 0.0086 0.671 13) 
 ("Pluto" 0.18 0.002 39.5 248.5 17.1 0.249 -6.5 3))
> 

 CSV文字列からリストへの変換で、文字列は文字列、数値は数値へと変換されているのに、お気付きでしょうか?これが自作したポイントです。
 これなら、EXCEL で作った表を CSVで保存すれば、newLISP で簡単に扱え、逆も可能です。

 以上、如何でしょうか?