Jock Cooper <jockc@mail.com> wrote:
+---------------
| Parsing a CSV line is nearly trivial, but can be a pain to handle the
| quote text qualifier and backslashes. Here is some code I wrote but
| was wondering if there might be a more elegant or shorter way:
|
| (defun get-csv-line (line)
| ...[61 lines omitted...)
+---------------
Here's mine, which I've been using for some time. It's about half
the length of yours (not that LOC is a good measure of anything),
uses a slightly simpler parser (though I admit I went through
*several* revisions before this version), and uses LOOP...ACROSS
to avoid having to break the string into a list of characters
up-front (though it probably still conses an equivalent amount):
;;; PARSE-CSV-LINE -- 2004-03-06/rpw3@rpw3.org
;;; Parse one CSV line into a list of fields, ignoring comment
;;; lines, stripping quotes and field-internal escape characters.
;;; Lexical states: '(normal quoted escaped quoted+escaped)
;;;
(defun parse-csv-line (line)
(when (or (string= line "") ; special-case blank lines
(char= #\# (char line 0))) ; or those starting with "#"
(return-from parse-csv-line '()))
(loop for c across line
with state = 'normal
and results = '()
and chars = '() do
(ecase state
((normal)
(case c
((#\") (setq state 'quoted))
((#\\) (setq state 'escaped))
((#\,)
(push (coerce (nreverse chars) 'string) results)
(setq chars '()))
(t (push c chars))))
((quoted)
(case c
((#\") (setq state 'normal))
((#\\) (setq state 'quoted+escaped))
(t (push c chars))))
((escaped) (push c chars) (setq state 'normal))
((quoted+escaped) (push c chars) (setq state 'quoted)))
finally
(progn
(push (coerce (nreverse chars) 'string) results) ; close open field
(return (nreverse results)))))
-Rob
-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607