RFC 4180対応版 CSVレコードの分解
Posted feedbacks - Common Lisp
arnesi:parse-csv-stringはyYyとなるバグがあって使えない
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | (require :fare-csv)
(defun splitCSV (line)
(loop for elt in (with-input-from-string (inn line)
(fare-csv:read-csv-line inn))
for i from 1 do
(format t "~a => ~a~%" i elt)))
(splitCSV "\"aaa\",\"b
bb\",\"ccc\",zzz,\"y\"\"Y\"\"y\",xxx
")
;; 1 => aaa
;; 2 => b
;; bb
;; 3 => ccc
;; 4 => zzz
;; 5 => y"Y"y
;; 6 => xxx
|
【これはひどい】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | (defun splitCSV (csv)
(let ((len (length csv))
(lst '(())))
(labels
((field (h n i &optional (esc nil))
(when (> len i)
(if h
(princ (format nil "~D => " n)))
(let ((it (char csv i)))
(case it
(#\" (if esc
(case (char csv (incf i))
(#\" (princ #\" ) (field nil n (1+ i) t))
(#\, (princ #\newline) (field t (1+ n) (1+ i))))
(field nil n (1+ i) t)))
(#\newline (if esc
(progn (princ it) (field nil n (1+ i) t))
(progn (princ it) (field t 1 (1+ i)))))
(#\, (princ #\newline) (field t (1+ n) (1+ i)))
(t (princ it) (field nil n (1+ i) esc)))))))
(field t 1 0 nil))))
|
色々修正版 (splitCSV "\"aaa\",\"b bb\",\"ccc\",zzz,\"y\"\"Y\"\"y\",xxx,\"eee,EEE\",,,") 1 => aaa 2 => b bb 3 => ccc 4 => zzz 5 => y"Y"y 6 => xxx 7 => eee,EEE 8 => 9 => nil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | (defun splitCSV (csv)
(let ((len (length csv)))
(labels
((field (h n i &optional (esc nil))
(when (> len i)
(and h (princ (format nil "~D => " n)))
(let ((it (char csv i)))
(case it
(#\" (if esc
(case (char csv (incf i))
(#\" (princ #\" ) (field nil n (1+ i) t))
(#\, (princ #\newline) (field t (1+ n) (1+ i))))
(field nil n (1+ i) t)))
(#\newline (if esc
(progn (princ it) (field nil n (1+ i) t))
(progn (princ it) (field t 1 (1+ i)))))
(#\, (if esc
(progn (princ it) (field nil n (1+ i) t))
(progn (princ #\newline) (field t (1+ n) (1+ i)))))
(t (princ it) (field nil n (1+ i) esc)))))))
(field t 1 0 nil))))
|
やっちゃった>< 相互呼び出しの再帰で実装してみた。 これって、10 => まで出るのが正しいんだよね? [sample.csv] "aaa","b bb","ccc",zzz,"y""Y""y",xxx,"eee,EEE",,, (splitCSV "sample.csv") 1 => aaa 2 => b bb 3 => ccc 4 => zzz 5 => y"Y"y 6 => xxx 7 => eee,EEE 8 => 9 => 10 => t
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | (defun splitCSV (csv-file)
(labels
((in-esc (csv header col)
(let ((it (read-char csv nil)))
(case it
((#\") (let ((it (read-char csv nil)))
(case it
(#\" (princ it) (in-esc csv nil col))
(#\, (princ #\newline) (out-esc csv t (1+ col))))))
((nil) nil)
(t (princ it) (in-esc csv nil col)))))
(out-esc (csv header col)
(let ((it (read-char csv nil)))
(and header it (format t "~D => " col))
(case it
((#\") (in-esc csv nil col))
((#\newline) (princ it) (out-esc csv t 1))
((#\,) (princ #\newline) (out-esc csv t (1+ col)))
((nil) t)
(t (princ it) (out-esc csv nil col))))))
(with-open-file (csv csv-file)
(out-esc csv t 1))))
|
題意に沿うバージョンと、ファイルから読み込むバージョンとに分けてみた。 #終端処理が微妙なことに気づいたので微妙に修正…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | (defun splitCSV (csv-stream)
(labels
((in-esc (csv header col)
(let ((it (read-char csv nil)))
(case it
((#\") (let ((it (read-char csv nil)))
(case it
(#\" (princ it) (in-esc csv nil col))
(#\, (princ #\newline) (out-esc csv t (1+ col))))))
((nil) nil)
(t (princ it) (in-esc csv nil col)))))
(out-esc (csv header col)
(let ((it (read-char csv nil)))
(and header (or (> col 1) it) (format t "~D => " col))
(case it
((#\") (in-esc csv nil col))
((#\newline) (princ it) (out-esc csv t 1))
((#\,) (princ #\newline) (out-esc csv t (1+ col)))
((nil) t)
(t (princ it) (out-esc csv nil col))))))
(and (streamp csv-stream)
(out-esc csv-stream t 1))))
(defun splitCSV-from-file (csv-file)
(with-open-file (stream csv-file)
(splitCSV stream)))
(defun splitCSV-from-string (csv-string)
(with-input-from-string (stream csv-string)
(splitCSV stream)))
|




raynstard
#3389()
Rating1/1=1.00
[ reply ]