···102102103103(* {1 String roundtrip (no commas/newlines)} *)
104104105105+let strip_leading_hash s =
106106+ let n = String.length s in
107107+ let i = ref 0 in
108108+ while !i < n && s.[!i] = '#' do
109109+ incr i
110110+ done;
111111+ if !i = 0 then s else String.sub s !i (n - !i)
112112+105113let test_string_roundtrip s =
106114 (* Filter characters that have special meaning in CSV: comma (field separator),
107107- newline/CR (row separator), and double-quote (quoting delimiter per RFC 4180).
108108- These characters require quoting to roundtrip, tested separately. *)
115115+ newline/CR (row separator), double-quote (quoting delimiter per RFC 4180),
116116+ and leading # (comment marker). These characters require quoting to
117117+ roundtrip, tested separately. *)
109118 let s =
110119 String.to_seq s
111120 |> Seq.filter (fun c -> c <> ',' && c <> '\n' && c <> '\r' && c <> '"')
112121 |> String.of_seq
113122 in
123123+ let s = strip_leading_hash s in
114124 if String.length s = 0 then () (* empty lines are skipped *)
115125 else
116126 let csv = "v\n" ^ s ^ "\n" in
···129139 |> Seq.filter (fun c -> c <> ',' && c <> '\n' && c <> '\r' && c <> '"')
130140 |> String.of_seq
131141 in
142142+ let s = strip_leading_hash s in
132143 if String.length s = 0 then ()
133144 else
134145 let csv = "a,b\n" ^ string_of_int i ^ "," ^ s ^ "\n" in
···147158 |> Seq.filter (fun c -> c <> ',' && c <> '\n' && c <> '\r' && c <> '"')
148159 |> String.of_seq
149160 in
161161+ let s = strip_leading_hash s in
150162 if String.length s = 0 then ()
151163 else
152164 let csv1 = "a,b\n" ^ string_of_int i ^ "," ^ s ^ "\n" in
+7-5
lib/csvt.ml
···618618619619let needs_quoting s =
620620 let n = String.length s in
621621- let rec loop i =
622622- if i >= n then false
623623- else match s.[i] with ',' | '"' | '\n' | '\r' -> true | _ -> loop (i + 1)
624624- in
625625- loop 0
621621+ if n > 0 && s.[0] = '#' then true
622622+ else
623623+ let rec loop i =
624624+ if i >= n then false
625625+ else match s.[i] with ',' | '"' | '\n' | '\r' -> true | _ -> loop (i + 1)
626626+ in
627627+ loop 0
626628627629let write_quoted_field w s =
628630 w "\"";
+3
lib/csvt.mli
···1313 share a single type {!t}. Row codecs are built declaratively using
1414 constructors and field accessors with the {!Row} builder.
15151616+ Lines starting with [#] are treated as comments and skipped during decoding
1717+ (both in the header section and in data rows). Empty lines are also skipped.
1818+1619 {2 Quick Start}
17201821 {v