Filtrage de fichiers avec ocaml

Anne ocaml awk

D’habitude, pour filtrer un fichier, j’utilise awk ou éventuellement perl, mais comme je développe par ailleurs en ocaml, il est parfois plus propre/simple de ne pas utiliser 50 outils différents. Voilà donc quelques notes pour retrouver rapidement comment on s’y prend en ocaml.

Lire un fichier

  let chan = open_in filename in
  try
    while true; do
      let line = input_line chan in
      ...
      lines := input_line chan :: !lines
    done; []
  with End_of_file -> close_in chan

Ne pas oublier de rattraper l’exception de open_in si besoin.

Si le fichier a un format de ligne fixe, on peut aussi utiliser le module Scanf. Par exemple, pour lire une ligne contenant deux entiers et une chaîne :

Scanf.fscanf chan "%d %d %s\n" (fun x y value -> (x, y, value))

Lire un csv

S’il s’agit d’un CSV sophistiqué, il vaut mieux utiliser une bibliothèque, mais si on veut lire un CSV tout simple, on peut faire :

let read_csv filename
  let separator = "," in
  let re_sep = Str.regexp separator in
  let ic = open_in filename in
  let nline = ref 0 in
  try
    while true; do
      incr nline;
      let line = input_line ic in
      if (!nline > 1) then
        let fields = Str.split_delim re_sep line in
        let fields = List.map String.trim fields in
        match fields with
        | name :: info1 :: info2 :: [] ->
          ()
        | _ -> failwith ("wrong number of columns at line "^(string_of_int !nline))
    done
  with End_of_file -> close_in ic

Ici, on lit un fichier à trois colonnes séparées par des virgules, et on saute la première ligne (titres).

Filtrage

Pour manipuler la ligne lues, on peut utiliser le module Str. Attention, ce module n’est par linké par défaut, il faut donc l’ajouter lors de la compilation:

$ ocamlc options str.cma other files

ou pour un simple script :

$ ocaml str.cma filter.ml

On peut filtrer une ligne de la façon suivante :

  let re = Str.regexp "^- \\([^:]*\\):" in
  if (Str.string_match re line 0) then
    let item = Str.matched_group 1 line in
    ...

La construction \\(\\) permet d’isoler des morceaux de l’expression que l’on retrouve ensuite avec Str.matched_group nn est le numéro de la sous-expression.

Le module Str fournit également des fonctions de remplacement comme global_replace :

let line = Str.global_replace (Str.regexp "xyz\\([0-9]*\\)") "coord:\1" line

et des fonctions de découpage en champs comme Str.split et diverses variantes.

D’autres manipulations de chaînes sont fournies par le module String, comme par exemple String.lowercase pour mettre une chaîne en minuscules, ou encore String.trim pour ôter les espaces au début et à la fin d’une chaîne.

Voir aussi :