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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
let parse_row separator line =
let len = String.length line in
let fields = ref [] in
let buf = Buffer.create 64 in
let i = ref 0 in
while !i < len do
if line.[!i] = '"' then (
incr i;
let in_quotes = ref true in
while !i < len && !in_quotes do
if line.[!i] = '"' then
if !i + 1 < len && line.[!i + 1] = '"' then (
Buffer.add_char buf '"';
i := !i + 2)
else (
in_quotes := false;
incr i)
else (
Buffer.add_char buf line.[!i];
incr i)
done;
if !i < len && line.[!i] = separator then incr i;
fields := Buffer.contents buf :: !fields;
Buffer.clear buf)
else if line.[!i] = separator then (
fields := Buffer.contents buf :: !fields;
Buffer.clear buf;
incr i)
else (
Buffer.add_char buf line.[!i];
incr i)
done;
fields := Buffer.contents buf :: !fields;
List.rev !fields
let strip_cr line =
let len = String.length line in
if len > 0 && line.[len - 1] = '\r' then String.sub line 0 (len - 1) else line
let parse ?(separator = ',') content =
let lines = String.split_on_char '\n' content in
let lines = List.map strip_cr lines in
let lines = List.filter (fun l -> l <> "") lines in
List.map (parse_row separator) lines
let needs_quoting separator field =
let len = String.length field in
let rec check i =
if i >= len then false
else
let c = field.[i] in
c = separator || c = '"' || c = '\n' || c = '\r' || check (i + 1)
in
check 0
let quote_field separator field =
if needs_quoting separator field then (
let buf = Buffer.create (String.length field + 4) in
Buffer.add_char buf '"';
String.iter
(fun c ->
if c = '"' then Buffer.add_string buf "\"\"" else Buffer.add_char buf c)
field;
Buffer.add_char buf '"';
Buffer.contents buf)
else field
let write_row buf separator fields =
List.iteri
(fun i field ->
if i > 0 then Buffer.add_char buf separator;
Buffer.add_string buf (quote_field separator field))
fields;
Buffer.add_char buf '\n'
let serialize ?(separator = ',') rows =
let buf = Buffer.create 1024 in
List.iter (write_row buf separator) rows;
Buffer.contents buf
let write_row_to_channel oc separator fields =
let buf = Buffer.create 256 in
write_row buf separator fields;
output_string oc (Buffer.contents buf)