sourcelibs/Csv/src/Csv.xtl

1⍝# Csv: comma-separated values -- lines to fields (quoted fields kept 2⍝# whole), a table of texts, columns by number or name, numbers, and 3⍝# back to text. 4⍝# Import it with an alias of your choice: "cs:" u_se< "Csv". 5⍝# Put libs/Csv/src on XETAL_PATH ("just path"); the reference is libs/Csv/docs. 6⍝# Names with l: are exported; those under h: are private to this file. 7⍝# 8⍝# A table is a matrix of texts (Box Char), a row per line, its first 9⍝# row the header. Fields follow RFC 4180: a field in double quotes may 10⍝# hold commas, and a doubled quote inside stands for one. 11 12ᵗ⁼u̲se< "Strings" 13"q:" u̲se< "Lists" 14 15dq ← "\"" ⍝ a double quote, as text 16 17⍝## Parsing 18 19⍝# A field as written to its value: outer quotes off, "" to ". 20ʰu̲nquote ← { f → 21 2 > t̲ally f ? f 22 n̲ot (dq m̲atch 1 t̲ake f) ∧ dq m̲atch -1 t̲ake f ? f 23 ((e̲nclose dq c̲at dq) c̲at e̲nclose dq) ᵗr̲eplace 1 d̲rop -1 d̲rop f 24} 25 26⍝# f_ields line: the fields of one line. A comma separates fields 27⍝# unless an odd number of quotes comes before it (it is inside a 28⍝# quoted field). 29ˡf̲ields ← { line → 30 inside ← 1 = ('+ s̲\ 0 + line = f̲irst dq) m̲od 2 31 cut ← w̲here (line = f̲irst ",") ∧ n̲ot inside 32 starts ← 1 c̲at cut + 1 33 ends ← cut c̲at 1 + t̲ally line 34 '{ i → ʰu̲nquote ((f̲irst i s̲elect ends) − f̲irst i s̲elect starts) t̲ake ((f̲irst i s̲elect starts) − 1) d̲rop line } m̲ap r̲ange t̲ally starts 35} 36 37⍝# r_ows text: the table of a whole text, a row per line (every line 38⍝# has the same number of fields). 39ˡr̲ows ← { text → 40 lines ← ᵗl̲ines text 41 rows ← '{ l → ˡf̲ields d̲isclose l } m̲ap lines 42 ((t̲ally lines) c̲at t̲ally d̲isclose f̲irst rows) r̲eshape q:r̲aze rows 43} 44 45⍝## Columns 46 47⍝# n c_olumn table: the texts of column n, the header left out. 48ˡc̲olumn ← { n table → 1 d̲rop n s̲elect₂ table } 49 50⍝# name f_ield table: the texts of the column whose header is name. 51ˡf̲ield ← { name table → (f̲irst (f̲irst table) i̲ndexOf e̲nclose name) ˡc̲olumn table } 52 53⍝# name n_umbers table: the column whose header is name, as numbers. 54ˡn̲umbers ← { name table → n̲umbers " " ᵗj̲oin name ˡf̲ield table } 55 56⍝## Writing 57 58⍝# A value as a field: quoted (quotes doubled) when it holds a comma, a 59⍝# quote or a newline. 60ʰq̲uote ← { v → 61 n̲ot '∨ r̲/ v m̲ember? ",\n" c̲at dq ? v 62 dq c̲at (((e̲nclose dq) c̲at e̲nclose dq c̲at dq) ᵗr̲eplace v) c̲at dq 63} 64 65⍝# t_ext table: the table as comma-separated text, a line per row. 66ˡt̲ext ← { table → 67 "\n" ᵗj̲oin '{ i → "," ᵗj̲oin '{ f → ʰq̲uote d̲isclose f } m̲ap i s̲elect table } m̲ap r̲ange t̲ally table 68}