Article publié dans Linux Magazine 68, janvier 2005.
Un de mes amis (webmestre de http://www.fatrazie.com/) possède un fichier avec près de 50 000 noms de villes françaises avec leurs coordonnées géographiques et leurs codes postaux. Ce fichier a été lui-même assemblé laborieusement à partir de diverses sources et à l'aide de programmes Perl (dont le module WWW::Gazetteer::HeavensAbove, que j'ai écrit pour l'occasion).
Le fichier courant contient une ville par ligne, avec dans l'ordre les champs nom, latitude, longitude, élévation et code postal, séparés par des tabulations. En voici un extrait :
Montluel 45.850 5.050 195 01120 Nièvroz 45.833 5.067 185 01120 Pizay 45.883 5.083 284 01120 Pizay 45.733 4.333 492 01120 Thil 45.817 5.017 182 01120 Sainte-Croix 44.767 5.283 425 01120 Sainte-Croix 45.900 5.050 280 01120 Sainte-Croix 44.767 5.283 425 01120 La Léchere 45.200 6.467 1075 01121 La Léchère 45.867 5.100 238 01121 La Léchère 45.867 5.100 238 01121 Léchère 45.583 6.333 1393 01121 Belleydoux 46.250 5.767 754 01130 Charix 46.183 5.683 758 01130
Pour « nettoyer » son fichier, il souhaite maintenant supprimer les doublons de villes ayant le même nom et le même code postal (les coordonnées géographiques sont souvent très proches, voire identiques).
L'objectif de cette perle n'est pas seulement de vous montrer l'uniligne qui a fait tout le travail, mais surtout de vous apprendre le réflexe presque pavlovien de tout perleur accompli : quand vous entendez le mot « unique », vous devez immédiatement penser « table de hachage ». Ensuite, tout le problème est de construire la bonne clé pour ce hachage.
Dans le cas qui nous occupe, c'est tout simple : on considère que deux villes sont identiques si elles ont le même nom et le même code postal. Notre clé sera donc la simple concaténation de ces deux champs.
Ce qui donne l'uniligne suivant :
$ perl -lnaF\\t -e 'print unless $c{$F[0].$F[-1]}++' FranceA-Z.txt > FranceA-unique.txt
Les options -a et -F sont expliquées dans perlrun(1) ainsi que dans
mon articles Écrire un programme d'une ligne en Perl, publié dans Linux
Magazine 50 et repris dans Linux Dossiers 2. Vous savez donc que le
tableau @F
contient les champs de la ligne en cours.
Notez que le caractère qui suit immédiatement l'option -F est une
tabulation, car c'est le caractère de séparation utilisé dans le fichier.
Il est protégé du shell par un \
.
L'utilisation de l'opérateur ++
postfixé fait que la première fois
qu'on rencontre une ligne produisant une certaine clé, la valeur dans
la table de hachage %c
est auto-vivifiée à undef
(donc fausse). Le
print()
est exécuté et la valeur est ensuite mise à 1
.
Et ça marche !
$ wc -l France* 50049 FranceA-Z.txt 35823 FranceA-Z-unique.txt $ diff FranceA-Z.txt FranceA-Z-unique.txt ... Montluel 45.850 5.050 195 01120 Nièvroz 45.833 5.067 185 01120 Pizay 45.883 5.083 284 01120 -Pizay 45.733 4.333 492 01120 Thil 45.817 5.017 182 01120 Sainte-Croix 44.767 5.283 425 01120 -Sainte-Croix 45.900 5.050 280 01120 -Sainte-Croix 44.767 5.283 425 01120 La Léchere 45.200 6.467 1075 01121 La Léchère 45.867 5.100 238 01121 -La Léchère 45.867 5.100 238 01121 Léchère 45.583 6.333 1393 01121 Belleydoux 46.250 5.767 754 01130 Charix 46.183 5.683 758 01130 ...
Attention quand vous utilisez des clés composites : contrairement
au cas ci-dessus, il est en général préférable d'utiliser un séparateur
spécifique entre ces clés. Cela permet d'éviter des collisions fâcheuses,
par exemple avec des cas où une clé serait la concaténation de ab
,
a
et l'autre celle de a
et ba
.
Le problème ne se posait pas dans notre cas, car il n'existe pas de ville dont le nom se termine par un nombre dans notre fichier.
Pour nous simplifier la vie, nous allons utiliser une technique remontant à Perl 4 : l'émulation de tableaux multi-dimensionnels (à l'époque, les références n'existaient pas et c'était la seule manière de faire des tableaux multi-dimensionnels). Cela consiste à séparer les différents éléments de la clé par des virgules.
Notre uniligne deviendrait (on a changé le .
en ,
) :
$ perl -lnaF\\t -e 'print unless $c{$F[0],$F[-1]}++' FranceA-Z.txt > FranceA-unique.txt
Perl remplace alors $c{$F[0],$F[-1]}
par $c{join $;, $F[0], $F[-1]}
,
comme expliqué dans perlvar(1) à la section parlant de la variable
$;
. Par défaut, $;
est le caractère \034
, qui a tout de même
peu de chances de se retrouver dans vos données.
(Philippe "BooK" Bruhat, Paris.pm & Lyon.pm - book@mongueurs.net
)
J'ai récemment dû faire le tri entre les « bonnes »
lignes
et les « mauvaises »
lignes d'un fichier. Le fichier en question était
la sortie de comm(1). Il s'agissait de vérifier que toutes les lignes
d'un fichier A étaient présentes dans le fichier B (A et B étant triés).
On utilise donc comm -2 A B
pour obtenir les lignes de A absentes de
B et les lignes de A présentes dans B. Ces dernières sont précédées
d'une tabulation puisque comm(1) présente les résultats en colonnes.
Pour distribuer les lignes dans les fichier A_ok et A_err, on utilise l'uniligne suivant :
comm -2 A B | perl -nle 'print{s/^\t//?STDOUT:STDERR}$_' > A_ok 2> A_err
Explication : on utilise l'opérateur ternaire ?:
pour choisir
vers quel filehandle écrire la ligne courante : la sortie standard
ou la sortie d'erreur. Le choix est conditionné par la présence d'une
tabulation en début de ligne, que l'on enlève au passage (s/^\t//
). Le
filehandle donné à print
doit être soit un mot simple (bareword),
soit une variable scalaire (sinon l'analyseur syntaxique de Perl
n'arrive pas à s'y retrouver). Toute chose plus compliquée que cela
(comme un élément de tableau ou une expression) doit être placée entre
accolades :
print { expression qui renvoie un filehandle } ...
Ensuite, on utilise le shell pour rediriger la sortie standard et la sortie d'erreur vers deux fichiers différents.
(Cédric Bouvier, Genève.pm - cbouvi@mongueurs.net
)
Maintenant que vous avez votre mini-CPAN (et qu'il est partagé afin
d'en faire profiter vos collègues), vous aimeriez y ajouter les
modules développés en interne et qui ne peuvent pas être publiés.
L'intérêt est de pouvoir les installer via la commande cpan
comme n'importe quel autre module public. Remerciez Shawn Sorichetti
d'avoir écrit CPAN::Mini::Inject
dont c'est justement le rôle.
La commande mcpani, installée avec le module, permet de gérer
son mini-CPAN encore plus simplement via un fichier de configuration
(que mcpani ira chercher dans $HOME/.mcpani/config,
/usr/local/etc/mcpani ou /etc/mcpani) :
local: /usr/local/cpan remote: http://your.cpan-mirror.net/ ftp://ftp.cpan.org/pub/CPAN/ repository: /work/perl/modules passive: yes
Les paramètres parlent d'eux-mêmes :
local
indique le répertoire où est stocké le mini-CPAN,
remote
les sites sur lesquels se synchroniser
et repository
le répertoire contenant les modules privés.
passive
indique d'utiliser le mode passif lors des transferts FTP.
L'intégration de modules privés passent d'abord par leur ajout dans le repository :
$ mcpani --add --module My::Great::Module --authorid JSUIFER \ --modversion 0.01 --file /home/jean/perl/My-Great-Module-0.01.tar.gz
où --module
précise le nom du module à ajouter, --modversion
sa version et --file
l'archive, dont le nom doit respecter les
conventions du CPAN. --authorid
précise l'identifiant CPAN de
l'auteur, mais qui n'a pas besoin d'exister officiellement dans ce
cas-ci. (CPAN::Mini::Inject
réalise ici une opération similaire à
ce que fait PAUSE lors de l'indexation d'un module déposé sur le CPAN.)
Les modules ajoutés à ce repository sont ensuite injectés dans le mini-CPAN par la commande :
$ mcpani --inject
Pour maintenir à jour votre mini-CPAN, il vous suffira d'exécuter
mcpani avec l'option --update
qui met d'abord à jour le mini-CPAN
puis réinjecte les modules non-publics du repository.
(Sébastien Aperghis-Tramoni, Marseille.pm - <sebastien@aperghis.net>)
L'avent. Après.
Pour les anglophones, mentionnons (certes un peu tard) le calendrier de l'avent offert chaque année depuis l'an 2000 à la communauté Perl par Mark Fowler, le leader de London.pm : http://perladvent.org/. Chaque jour de l'avent, il y présente un module de CPAN, avec quelques explications.
Mark a donc remis ça en cette année 2004 et les premiers
modules présentés furent : DateTime
, Term::ANSIColor
,
Class::Accessor::Chained
, String::ShellQuote
, CPAN::Mini
,
Module::Pluggable
, Term::ProgressBar
...
Consultez le site pour voir la liste complète (ainsi que les listes des années précédentes) et découvrir des modules que vous ne connaissiez peut-être pas !
(Stéphane Payrard, Paris.pm - <stef@mongueurs.net>)
Envoyez vos perles à perles@mongueurs.net
, elles seront peut-être
publiées dans un prochain numéro de Linux Magazine.
Copyright © Les Mongueurs de Perl, 2001-2011
pour le site.
Les auteurs conservent le copyright de leurs articles.