練習問題 10.1 wcコマンド
UNIX の wc コマンドは、ファイル名の列を引数として、それぞれのファイルの行数(改行文字の数)、空白で区切られた単語の数、バイト数を表示するものです。これを OCaml で実装し、実行可能ファイルを作成しなさい。オプションとしては、バイト数だけ表示する -c、行数だけを表示する -l、単語数だけを表示する -w を実装しなさい。
解答
let ver = "0.1";;
let count_lines = ref false (* 行数を表示するかどうかを示す *)
and count_bytes = ref false
and count_words = ref false;;
let filenames = ref [];; (* 処理するファイル名をためておく *)
let spec = [("-l", Arg.Set count_lines, "Display number of lines");
("-c", Arg.Set count_bytes, "Display number of bytes");
("-w", Arg.Set count_words, "Display number of words");
("-version",
Arg.Unit
(fun () -> Printf.printf "cat in OCaml ver: %s\n" ver),
"Display version number")];;
let rec assoc a = function
[] -> ""
| (a', b) :: rest -> if a = a' then b else assoc a rest;;
let display_count filename =
let oc = open_in filename
and lines = ref 0
and bytes = ref 0
and words = ref 0
and moji = ref 'a'
and end_of_file = ref false in
while (!end_of_file == false) do
try
moji := input_char oc;
bytes := !bytes + 1;
if !moji = ' ' then words := !words + 1;
if !moji = '\n' then lines := !lines + 1
with End_of_file -> end_of_file := true
done;
close_in oc;
[("bytes", string_of_int !bytes);
("words", string_of_int !words);
("lines", string_of_int !lines)];;
let _ =
Arg.parse spec
(fun s -> filenames := s :: !filenames)
"Usage: wc [-c] [-w] [-l] [-help] [-version] filename ...";
(* この時点で display_linenum, filenames が変更されているはず *)
List.iter
(fun s ->
if !count_bytes
then Printf.printf "C:%s " (assoc "bytes" (display_count s));
if !count_words
then Printf.printf "W:%s " (assoc "words" (display_count s));
if !count_lines
then Printf.printf "L:%s " (assoc "lines" (display_count s));
Printf.printf "filename: %s\n" s)
(List.rev !filenames);;