|
| 1 | +(ns basilisp.csv |
| 2 | + "Wrapper functions for :external:py:mod:`csv` which reads and writes CSVs." |
| 3 | + (:require |
| 4 | + [basilisp.io :as bio]) |
| 5 | + (:import |
| 6 | + csv)) |
| 7 | + |
| 8 | +(defn read-csv |
| 9 | + "Returns a lazy sequence of vectors corresponding to the rows of a CSV file. |
| 10 | + |
| 11 | + If ``input`` is a string, it will be treated as the contents of a CSV. Otherwise, |
| 12 | + it will be treated as a :external:py:class:`io.TextIOBase`. |
| 13 | + |
| 14 | + The reader function supports the following keyword arguments: |
| 15 | + |
| 16 | + :keyword ``:separator``: the character used as the separator for a fields in a row |
| 17 | + default is \",\" |
| 18 | + :keyword ``:quote``: the quote character used on quoted fields; default is ``true``" |
| 19 | + [input & {:keys [separator quote] :or {separator "," quote "\""}}] |
| 20 | + (let [f (if (string? input) |
| 21 | + (io/StringIO input) |
| 22 | + input) |
| 23 | + reader (csv/reader f ** :delimiter separator :quotechar quote) |
| 24 | + do-read (fn read-csv* |
| 25 | + [] |
| 26 | + (when-some [row (python/next reader nil)] |
| 27 | + (cons (vec row) (lazy-seq (read-csv*)))))] |
| 28 | + (lazy-seq (do-read)))) |
| 29 | + |
| 30 | +(defn rows->maps |
| 31 | + "Returns a lazy sequence mapping over a sequence of CSV data (as vectors) |
| 32 | + converting them to maps. |
| 33 | + |
| 34 | + The first row will be used as the headers. The ``:key-fn`` keyword argument |
| 35 | + names a function which will be used to map over the keys." |
| 36 | + [rows & {:keys [key-fn] :or {key-fn keyword}}] |
| 37 | + (let [header (mapv key-fn (first rows))] |
| 38 | + (map #(zipmap header %) (rest rows)))) |
| 39 | + |
| 40 | +(defn write-csv |
| 41 | + "Write a sequence of rows in CSV format to ``writer``, which should be an |
| 42 | + :external:py:class:`io.TextIOBase`. |
| 43 | + |
| 44 | + The writing function supports the following keyword arguments: |
| 45 | + |
| 46 | + :keyword ``:separator``: the character used as the separator for a fields in a row |
| 47 | + default is \",\" |
| 48 | + :keyword ``:quote``: the quote character used on quoted fields; default is ``true`` |
| 49 | + :keyword ``:newline``: the newline character to use between rows (``:lf`` or |
| 50 | + ``:cr+lf``)" |
| 51 | + [writer data & {:keys [separator quote newline] :or {separator "," quote "\"" newline :lf}}] |
| 52 | + (let [nl (cond |
| 53 | + (= newline :lf) "\n" |
| 54 | + (= newline :cr+lf) "\r\n" |
| 55 | + :else (throw |
| 56 | + (ex-info "newline must be one of: :lf or :cr+lf" |
| 57 | + {:newline newline}))) |
| 58 | + w (csv/writer writer ** :delimiter separator :quotechar quote :lineterminator nl)] |
| 59 | + (.writerows w (map #(mapv str %) data)))) |
0 commit comments