Téléchargez l'archive des programmes perl présentés.
Solution de l'exercice "numérotation des lignes d'un fichier"
Si l'énoncé n'avait pas demandé que les numéros de ligne soient biens cadrés, on aurait pu, sous Unix, se contenter d'utiliser la commande grep -n "" nom_fichier.
Une solution classique vérifie l'existence du paramètre, l'ouverture possible du fichier avant de nommer $lig la ligne vue dans le fichier avec une variable explicite nommée $numero qu'on incrémente dans la boucle de lecture, soit le programme :
# test des paramètres if ($ARGV[0] eq "") { print " syntaxe : perl numerote.pl nom_de_fichier \n" ; exit(-1) ; } ; # fin de test sur les arguments $fichier = $ARGV[0] ; # récupération du nom du fichier # ouverture du fichier open( FIC ,"<$fichier") || die "\n impossible d'ouvrir le fichier nommé $fichier \n\n" ; # affichage numéroté $numero = 0 ; while ($lig=<FIC>) { $numero++ ; print sprintf(" %04d ",$numero).$lig ; } ; # fin de tant queUne écriture plus "perlienne" vient utiliser les variables spéciales aux noms barbares à savoir $. et $_ :
die(" syntaxe : perl numerote.pl nom_de_fichier \n ") if ($ARGV[0] eq "") ; open( FIC ,"<$ARGV[0]") or die "\n impossible d'ouvrir le fichier nommé $ARGV[0] \n\n" ; while (<FIC>) { print sprintf(" %04d ",$.).$_ ; } ;Solution de l'exercice "variables d'environnement"
Une solution classique consiste à stocker dans un fichier-texte le résultat de la commande set puis à parcourir les lignes et à les découper (le séparateur est le symbole "égale"), soit le programme :
print "\n Variable Valeur\n\n" ; $ficenv = "set.tmp" ; # sauvegarde dans un fichier `set > $ficenv ` ; # parcours du fichier open(FENV,"<$ficenv") or die ("impossible d'ouvrir $ficenv") ; $nbve = 0 ; while ($ligne=<FENV>) { $nbve++ ; chop($ligne) ; @ligenv = split(/=/,$ligne) ; ($tvar[$nbve],$tval[$nbve]) = @ligenv ; } ; # fin de tant que # affichage for ($idve=1;$idve<=$nbve;$idve++) { print sprintf(" %-20s",$tvar[$idve]) .sprintf("%-40s",$tval[$idve])."\n" ; } ; # fin de pour chaqueRemarque : au lieu de@ligenv = split(/=/,$ligne) ; ($tvar[$nbve],$tval[$nbve]) = @ligenv ;on aurait pu écrire :($tvar[$nbve],$tval[$nbve]) = split(/=/,$ligne) ;Une solution plus "perliste" consiste à savoir que la table de hachage nommée %ENV contient les variables d'environnement et leurs valeurs. On remarquera qu'on trie au passage (ce qui n'est sans doute pas nécessaire) les clés sans utiliser keys car Perl s'en doute !
print "\n Variable Valeur\n\n" ; foreach $ve (sort %ENV) { if (defined $ENV{$ve}) { print sprintf(" %-20s",$ve) .sprintf("%-40s",$ENV{$ve})."\n" ; } ; # fin de si variable définie } ; # fin de pour chaque
Pour l'affichage en cgi avec de la couleur, nous renvoyons le lecteur au code-source de la page http://www.info.univ-angers.fr/scripts/env.pl
Solution de l'exercice "dictionnaires d'un fichier-texte"
Nous laissons (comme nouvel exercice) le soin au lecteur de détailler les instructions Perl utilisées. A part =~, tr et s/ toutes ces instructions ont déja été vues
...
# test des paramètres if ($ARGV[0] eq "") { print " syntaxe : perl dicos.pl nom_de_fichier \n" ; exit(-1) ; } ; # fin de test sur les arguments $fictxt = $ARGV[0] ; # récupération du nom du fichier # ouverture du fichier open( FICT ,"<$fictxt") || die "\n impossible d'ouvrir le fichier nommé $fictxt \n\n" ; # parcours du fichier, remplissage du hachage au passage $nbLig = 0 ; # nombre de lignes du fichier $nbMot = 0 ; # nombre de mots en tout $nbMdi = 0 ; # nombre de mots différents while ($ligne=<FICT>) { $nbLig++ ; chop($ligne) ; # on élimine la ponctuation $ligne =~ tr/,.:!'"();/ / ; # on élimine le double tiret $ligne =~ s/--//g ; @mots = split(/ /,$ligne) ; foreach $m (@mots) { if (length($m)>0) { $nbMot++; $cntMot{$m}++ ; if ($cntMot{$m}==1) { $nbMdi++ ; } ; # finsi nouveau mot } ; # finsi mot non vide } ; # fin pour chaque mot } ; # fin de tant que print " Analyse du fichier $fictxt :\n" ; print " $nbLig ligne(s), $nbMot mot(s) dont $nbMdi mot(s) différent(s).\n" ; $dicNom = "dic_$fictxt.mot" ; # fichier alphabétique $dicOcc = "dic_$fictxt.occ" ; # fichier fréquenciel open(DICM,">$dicNom ") or die ("impossible d'écrire dans $dicNom") ; print DICM "fichier $dicNom issu de $0 $fictxt\n" ; foreach $m (sort keys(%cntMot)) { print DICM sprintf(" %-20s",$m) .sprintf("%4d",$cntMot{$m})."\n" ; # on en profite pour remplir un tableau pour les occurences $cle = sprintf("%04d",$cntMot{$m})."_$m" ; $tabMot{$cle}=$m ; } ; # fin pour chaque close(DICM) ; open(DICK,">$dicOcc ") or die ("impossible d'écrire dans $dicOcc") ; print DICK "fichier $dicOcc issu de $0 $fictxt\n" ; foreach $c (reverse sort keys(%tabMot)) { # on sait comment Perl convertit les %s en %c, on en profite : print DICK sprintf(" %-20s",$tabMot{$c}) .sprintf("%4d",$c)."\n" ; } ; # fin pour chaque close(DICK) ; print " Vous pouvez consulter les fichiers $dicNom et $dicOcc \n" ; print " dont voici le début : \n\n" ; system("head -n 20 $dicNom") ; system("head $dicOcc") ;
Solution de l'exercice "calcul de Chi2"
Installer un module consiste à recopier le fichier-texte correspondant dans "le bon répertoire". Lorsqu'on est root, ceci peut se faire en ligne de commande. Si on dispose des droits suffisants, pour notre exemple, on écrit seulement
perl -MCPAN -e 'install Statistics::Distributions'L'utilisateur courant peut alors se contenter d'écrireuse Statistics::Distributions ; $df = 17 ; $pr = 0.05 ; $vc = Statistics::Distributions::chisqrdistr ($df,$pr); print " valeur de chi2 : $vc \n" ;Si par contre on de dispose pas des droits root, il est possible de copier le fichier localement et de l'indiquer à perl. Par exemple si on crée le répertoire /home/info/gh/Bin/Perl_lib/, si on y crée ensuite le répertoire /home/info/gh/Bin//Perl_lib/Statistics et si on met le fichier Distributions.pm dans ce répertoire Statistics alors il suffit d'ajouter au début du programme perl précédent la ligneuse lib "/home/info/gh/Bin/Perl_lib" ;pour que perl aille chercher ce qu'il lui manque comme module à l'endroit indiqué.Solution de l'exercice "fichiers DBF"
Le format Dbase et plus généralement le format Xbase sont des formats qui permettent de stocker "proprement" des tableaux rectangulaires de données. Sans être des fichiers de base de données lourdes comme Oracle ou Sql, ces fichiers sont lisibles par la plupart des logiciels comme Excel ou les autres tableurs courant. Leur grand intérêt est d'utiliser comme unité de base de structure les colonnes (ou "champs"). Ils garantissent donc la constance du nombre de champs sur l'ensemble des lignes et l'homogénéité des données à l'intérieur d'une même colonne. De plus ils sont très économiques en terme de stockage car après la définition des colonnes on trouve les données bout à bout sans caractère de controle. Ainsi pour 18 lignes de 59 caractères soit 1062 octets, le fichier DBF fait 1384 octets (soit moins de 2 kO) alors que le même fichier lu sous Excel 10 et enregistré au format XLS fait 14 384 octets soit plus de 14 kO c'est à dire 10 fois plus !. Après avoir lu la page de man de XBASE disponible ici il est relativement simple d'écrire le programme dbase.pl suivant :
use lib "/home/gh/Bin"; use XBase ; $nomBase = "vins.dbf" ; $table = new XBase $nomBase or die XBase->errstr; $nbr = $table->last_record ; print " il y a ".(1+$nbr)." enregistrements dans la base $nomBase ; for (0 .. $nbr) { print sprintf("%5d. ",1+$_) ; @enrc = $table->get_record($_) ; $ndc = -1 ; foreach $ch (@enrc) { $ndc++ ; if ($ndc>0) { print "$ch" ; } } ; # fin pour chaque champ print "\n \n" ; } ; # fin pour enregistrementSi on l'applique au fichier vins.dbf proposé, on voit alors :il y a 18 enregistrements dans la base vins.dbf 1. CHMP 7069 3786 12578 8037 13556 9664 10386 206 2. MOS1 2436 586 2006 30 1217 471 997 51 3. MOS2 3066 290 10439 1413 7214 112 3788 330 4. ALSA 2422 1999 17183 57 1127 600 408 241 5. GIRO 22986 22183 21023 56 30025 6544 13114 3447 6. BOJO 17465 19840 72977 2364 39919 17327 17487 2346 7. BORG 3784 2339 4828 98 7885 3191 11791 1188 8. RHON 7950 10537 7552 24 8172 11691 1369 1798 9. ANJO 2587 600 2101 0 7582 143 872 131 10. AOCX 17200 22806 15979 50 20004 1279 4016 944 11. VDQS 1976 1029 1346 0 2258 212 1017 487 12. XXXX 38747 19151 191140 7992 101108 1029 26192 38503 13. PROV 1375 1150 2514 0 284 401 9 236 14. MUSC 2016 2908 1529 0 12891 18 716 653 15. RHOF 785 1648 1009 6 775 643 542 35 16. AOCF 160 246 135 8 1177 26 7 0 17. XXXF 24 1533 160 0 480 0 0 0 18. XXFF 2415 74 208 8 1705 12 36 47En, rajoutant les instructions de tableau TR, TD etc. (voir le programme dbaset.pl) on obtient alors le joli affichage de la page dbaset.htmSolution de l'exercice "extractions d'adresses"
Lorsqu'on met .* perl cherche à satisfaire l'expression régulière en mode gourmand sans garder une trace de ce qui correspond. Mettre ? permet de satisfaire l'expression régulière en mode modéré et utiliser les parenthèses met en mémoire ce qui correspond dans les variables $1, $2 etc.
Ici, on veut dans une ligne comme <a href="www.google.fr">google</a> récupérer les champs entre <a href=" et le guillemet suivant et récupérer aussi ce qu'il y a entre le > et </a> ; comme on veut pas récupérer ce qu'il y a entre les deux (comme par exemple target="_blank" ) on ne met pas de parenthèses.
Si on met +=1 l'affectation est faite après l'affichage après et on ne voit que les références utilisées aumoins 2 fois, soit :
Homo sapiens http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Homo+sapiens Mus musculus http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Mus+musculus Sus scrofa http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Sus+scrofa Pseudomonas aeruginosa http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Pseudomonas+aeruginosa Escherichia coli http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Escherichia+coli Bos taurus http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Bos+taurus Bacillus stearothermophilus http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Bacillus+stearothermophilus Spinacia oleracea http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Spinacia+oleraceaPour trier les affichages, on peut écrire
while (m|<a href=\"(.*?)\".*?>(.*?)</a>>|gs) { $nom{$2}++ ; if ($nom{$2}==1) { $sites{$2} = $1 ; } ; } ; # fin tant que foreach $lien (sort keys %sites) { print " ".sprintf("%-30s %-30s",$lien, $sites{$lien}); } ; # fin pour chaqueRetour au tuteur Perl (gH)
Retour à la page principale de (gH)