1 (* Handy tool for managing CSV files.
2 * $Id: csvtool.ml,v 1.2 2005-11-25 14:06:58 rich Exp $
8 let cmd_cols ~separator ~csv ~chan cols =
9 let cols = List.map int_of_string cols in
11 let output = List.map (
13 let n = List.length row in
16 if 0 <= col_wanted && col_wanted < n then
17 List.nth row col_wanted
23 save_out ~separator chan output
25 let cmd_namedcols ~separator ~csv ~chan names =
28 | [] -> failwith "no rows in this CSV file"
30 let data = associate header data in
32 fun row -> List.map (fun name -> List.assoc name row) names
34 save_out ~separator chan data
36 let cmd_width ~csv ~chan () =
37 fprintf chan "%d\n" (columns csv)
39 let cmd_height ~csv ~chan () =
40 fprintf chan "%d\n" (lines csv)
42 let cmd_readable ~csv ~chan () =
43 save_out_readable chan csv
45 let cmd_square ~separator ~csv ~chan () =
46 let csv = square csv in
47 save_out ~separator chan csv
49 let cmd_sub ~separator ~csv ~chan args =
50 let r, c, rows, cols =
52 | [ r; c; rows; cols ] ->
53 int_of_string r, int_of_string c,
54 int_of_string rows, int_of_string cols
56 failwith "unknown arguments to 'sub' command" in
57 let csv = sub r c rows cols csv in
58 save_out ~separator chan csv
60 (* Process the arguments. *)
62 "csvtool - Copyright (C) 2005 Richard W.M. Jones, Merjis Ltd.
64 csvtool is a tool for performing manipulations on CSV files from shell scripts.
67 csvtool [-options] command [command-args] < input.csv
71 Return one or more columns from the CSV file. Columns are numbered
74 namedcol [name1] [name2] ...
75 Assuming the first row of the CSV file is a list of column headings,
76 this returned the column(s) with the named headings.
79 Return the maximum width of the CSV file (number of columns in the
83 Return the number of rows in the CSV file.
86 Print the input CSV in a readable format.
89 Make the CSV square, so all rows have the same length.
92 Take a square subset of the CSV, top left at row r, column c (counting
93 from 0), which is rows deep and cols wide.
95 Input and output files:
96 csvtool normally processes its input from stdin and writes its output
97 to stdout. Use the -i and -o options to override this behaviour.
102 let input_sep = ref ',' in
103 let set_input_sep = function
104 | "TAB" -> input_sep := '\t'
105 | "COMMA" -> input_sep := ','
106 | s -> input_sep := s.[0]
109 let output_sep = ref ',' in
110 let set_output_sep = function
111 | "TAB" -> output_sep := '\t'
112 | "COMMA" -> output_sep := ','
113 | s -> output_sep := s.[0]
116 let input_file = ref "" in
117 let output_file = ref "" in
120 "-t", Arg.String set_input_sep,
121 "Input separator char. Use -t TAB for tab separated input.";
122 "-u", Arg.String set_output_sep,
123 "Output separator char. Use -t TAB for tab separated output.";
124 "-i", Arg.Set_string input_file,
125 "Read CSV input from file (instead of stdin)";
126 "-o", Arg.Set_string output_file,
127 "Write output to file (instead of stdout)"
135 Arg.parse argspec set_rest usage;
137 let input_sep = !input_sep in
138 let output_sep = !output_sep in
139 let input_file = !input_file in
140 let output_file = !output_file in
141 let rest = List.rev !rest in
145 | [] -> prerr_endline (Sys.executable_name ^ " --help for usage"); exit 1
148 (* Read the input file. *)
150 if input_file <> "" then load ~separator:input_sep input_file
151 else load_in ~separator:input_sep stdin in
153 (* Set up the output file. *)
155 if output_file <> "" then open_out output_file
160 cmd_cols ~separator:output_sep ~csv:input ~chan args
161 | "namedcol" | "namedcols" ->
162 cmd_namedcols ~separator:output_sep ~csv:input ~chan args
164 cmd_width ~csv:input ~chan ()
166 cmd_height ~csv:input ~chan ()
168 cmd_readable ~csv:input ~chan ()
170 cmd_square ~separator:output_sep ~csv:input ~chan ()
172 cmd_sub ~separator:output_sep ~csv:input ~chan args
173 | _ -> prerr_endline (Sys.executable_name ^ " --help for usage")
176 if output_file <> "" then close_out chan