Tuteur PERL (PERL Tutorial)
Texte du Tuteur écrit par Gilles HUNAULT
Liste des autres tuteurs (langages, logiciels, systèmes d'exploitations...)
Texte remanié à partir du tuteur
écrit par les étudiantes de Licence MASS
Candice CHAUVE et Véronique COLIN.
Attention : ce tuteur date du début des années 2000 et traite de Perl 5 principalement. Il sera dépassé lorsque Perl 6 sera pleinement opérationnel (ce texte est écrit en janvier 2011). Pour un texte plus adapté, nous vous conseillons aux Editions Pearson :
Perl moderne
(19 euros)Pour aller plus loin, vous pouvez
lire un cours en français et détaillé (100 pages),
consulter la page Web de référence sur Perl/cgi (en anglais, avec index),
imprimer une carte de référence rapide pour Perl (anglais, 2 pages) dont une copie locale est ici,
imprimer une carte de référence détaillée pour Perl (anglais, 31 pages), dont une copie locale est ici,
Et si vous connaissez déjà Php, vous pouvez profiter du tableau synoptique des instructions Perl et Php.
Vous y trouverez aussi des exemples d'utilisation de Perl en Cgi et d'interfaçage avec des bases de données (comme MySql).
Table des Matières du tuteur
- Le langage Perl
- Quelques exemples classiques et courts de programmation en Perl
- Quelques exemples moins triviaux en Perl
- Exercices (solution)
1. LE LANGAGE PERL
INTRODUCTION
Le langage Perl a été inventé par le linguiste Larry WALL. Perl signifie (entre autres) Practical Extraction and Report Language. Il est gratuit et disponible sous tous les environnements (Unix, Windows, MacOs...) par exemple à partir du site officiel http://www.cpan.org, de http://www.perl.com ou de http://www.activestate.com (ActivePerl).
Conçu au départ pour la gestion de fichiers textes comme successeur de sed et Awk, il est rapidement devenu un langage de scripts indispensable notamment pour les scripts Web (CGI), les scripts de configuration (dont Apache).
Le langage de base de Perl est facile à appendre quoiqu'un peu "confusant" au départ à cause de symboles #, @ et $ un peu partout dans les variables ainsi qu'aux syntaxes obscures des expressions régulières.
Les milliers (si, si) de bibliothèques de sous-programmes (on dit en fait modules) du site officiel http://www.cpan.com permettent de développer rapidement une application sans avoir à tout inventer. Pour s'en convaincre, on peut consulter la liste des 7400 et quelques modules (valeur au premier janvier 2005) disponibles.
Les points forts de Perl sont la concision des programmes, l'utilisation massive des expressions régulières, le support des listes et des tables de hachage en tous genres et l'interfaçage avec le système d'exploitation.
Toutes les variables de Perl sont repérées par leur nom, préfixé par un sigil
- le symbole $ pour une variable "scalaire" c'est à dire simple,
- le symbole @ pour une variable tableau classique ou liste,
- le symbole % pour une variable tableau associatif ou "hachage".
Par exemple la variable jour est écrite $jour. Perl fait la distinction, comme Awk entre minuscules et majuscules. Ainsi la variable $Premier est différente des variables $premier et $PREmier.
Les commentaires commencent par le symbole #. Ils sont placés où l'on veut sur la ligne. Tous les caractères situés derrière le symbole # seront ignorés jusqu'à la fin de la ligne. La fin d'une instruction est matérialisée par le symbole ; et on peut bien sur écrire une instruction sur plusieurs lignes.
On notera qu'il ya de nombreuses variables prédéfinies aux noms "égyptiens" comme $_ $\ $# ... .
L'instruction print permet d'écrire, aussi bien à l'écran que dans un fichier.
Les doubles cotes " autour d'une expression induisent une interprétation des variables et des autres "séquences interprétables" contenues dans l'expression, ce qui signifie que Perl remplace les variables et les séquences par leur valeur. Par contre les simples cotes ' autour d'une expression interdisent cette interprétation. Les anti-cotes ` autour d'une expression transmettent l'expression au système d'exploitation.
Par exemple, si la variable $nom contient "Pierre" et si $age vaut 13 alors l'instruction
print '$nom\n a $age ans.';
affichera à l'écran :$nom\n a $age ans.
alors que l'instructionprint "$nom\n a $age ans.";
affichera à l'écran :Pierre a 13 ans.
car la séquence \n signifie "new line" c'est à dire passer à la ligne.Certains séquences font de la conversion de caractères. Par exemple \U passe en majuscules ("upper") jusqu'à la prochaine séquence et \E mis pour "end". Ainsi
print "le \Ugars $nom\E est jeune";
provoque l'affichage dele GARS PIERRE est jeune
Perl dispose de nombreuses options de lignes de commandes, d'un debugger et d'une "floppée" d'instructions pour produire des rapports (ce pour quoi il était conçu et qu'on utilise peu en fait). Ainsi print << MRK à l'intérieur d'un programme Perl recopie tout ce qui suit tel quel à l'écran jusqu'à rencontrer la chaine MRK. Une utilisation intensive de cette technique permet de créer des fichiers postscript à la volée (via Tex) comme notre production de pages de titres nommée titre.prl.
C'est un jeu d'enfants que d'écrire un mini-interpréteur Perl en Perl. la preuve : exécutez notre programme pt.pl. Pour cela il suffit de récupérer le fichier et de taper
perl pt.pl
TYPES DE DONNEES
Une variable peut désigner :
- un nombre, entier ou réel,
- une chaîne de caractères,
- un tableau,
- une liste,
- une table de hachage,
- un fichier,
- une référence (pointeur) à une variable,
- une désignation (glob) de variables.
Perl convertit automatiquement les types :
ainsi pour $val1 = "123"; et $val2 = $val1 + 2; la variable $val2 est égale à 125.
Perl essaie de convertir selon des règles techniques ; ainsi
@a = (1,"oui",4) ; print "@a\n" ; # affiche 1 oui 4 @b = @a ; print "@b\n" ; # affiche 1 oui 4 $c = @a ; print "$c\n" ; # affiche 3 car il y a 3 éléments dans @a $d = $a ; print "$d\n" ; # affiche une ligne vide $e = $#a ; print "$e\n" ; # affiche 2 car c'est l'indice du dernier # élément (on compte à partir de 0)
OPERATEURS
Les opérateurs arithmétiques sont classiques et supportent les raccourcis d'affectation comme en C :
addition + ++ soustraction - -- multiplication * division / modulo % exponentiation ** Les opérateurs sur les chaînes sont similaires à ceux de php :
Les opérateurs logiques distinguent les numériques des chaines de caractères.
concaténation . extraction de sous-chaîne substr
comparaison nombres chaînes de caractères égalité == eq inégalité != ne plus grand que > gt plus grand ou égal >= ge plus petit que < lt plus petit ou égal <= le comparaison avec
résultat signé<=> cmp Le résultat des opérations <=> et cmp est
-1 si l'opérande de gauche est inférieure à l'opérande de droite, 0 si elles sont égales, +1 si l'opérande de gauche est supérieure à l'opérande de droite. Les expressions logiques utilisent plusieurs opérateurs pour la même action, mais avec des préférences différentes ; voir la référence correspondante en ligne pour plus de détail. On y trouvera aussi la liste des trente et quelques tests logiques sur la présence ou les attributs des fichiers :
et logique
& && and ou logique
| || or Ces fonctions logiques fonctionnent en "court-circuit" c'est à dire qu'elles n'évaluent pas la seconde condition si la première suffit à déterminer le résultat du test logique.
INSTRUCTIONS
Un bloc d'instructions est délimité en Perl par des accolades.
Sous UNIX, on peut commencer un programme Perl par la ligne :
#! /usr/bin/perl
Cela permet, si le fichier est exécutable (chmod +x) de l'exécuter en tapant directement son nom, au lieu d'utiliser la syntaxe/usr/bin/perl nom_du_programme [paramètres éventuels]
L'affectation directe se fait par le symbole = alors que l'affectation par addition (ou "incrémentation automatique") se fait par += ; de même, l'affectation par soustraction se fait par -=. Le lecteur trouvera de lui-même la syntaxe pour les autres "auto-opérations".
Pour lire une variable sur l'entrée standard on écrit
$var = <STDIN> ;
Avec cette syntaxe $var contient le retour chariot. Pour palier à ce problème, il suffit d'utiliser l'une des fonctions suivantes :
chop($var); qui supprime le dernier caractère de $var. chomp($var); qui supprime le retour chariot de $var. Pour écrire sur la sortie standard on a le choix entre
print $var ; écrit sans retour à la ligne print $var."\n" ; écrit avec retour à la ligne print "$var\n" ; écrit avec retour à la ligne print "\U$var\E" ; écrit avec passage en majuscules Les tests et boucles sont traditionnels. La condition si utilise les syntaxes
if ( condition ) { ... }
etif ( condition ) { ... } else { ... }
On peut, pour les conditions imbriquées utiliser le mot-clé elsif :if ( condition ) { ... } elsif ( cond1 ) { ... } elsif ( cond2 ) { ... } .... else { ... } ;
Les boucles tant que s'écriventwhile ( condition(s) ) { ... }
alors que les boucles pour suivent la syntaxe :for ( cond1; cond2; cond3 ) { ... }
Perl dispose d'autres boucles. Signalons la boucle jusqu'à qui s'écrit
{ ... } until ( condition(s) )
et les boucles de parcours des éléments d'un tableauforeach élément ( liste ou tableau ) { ... } while ((clé,valeur) = each(tableau_associatif))
Perl dispose de deux opérateurs annexes next qui permet de passer à la fin de l'itération courante et d'en commencer une nouvelle ainsi que last qui permet de sortir de la boucle.
Perl permet de faire appel à une commande du système grâce à la commande system (mais on peut aussi utiliser les "anti-cotes" avec la combinaison de touches AltGr-è). Il y a souvent plusieurs façons d'écrire les choses en perl. Ainsi on peut quitter un programme avec un code de retour grace à exit mais die fait la même chose avec la possibilité d'écrire un message. De même, unless permet d'inverser le test en si. On notera que chaque instruction renvoie une valeur logique ce qui permet d'écrire des lignes comme
open( FIC ,"<$ARGV[0]") or die "\n impossible d'ouvrir le fichier ... \n\n" ;
EXPRESSIONS REGULIERES
Les expressions régulières (ou modèles de chaines de caractères) et les séquences interprétables rendent Perl très efficace dans la gestion des chaines de caractères. La comparaison d'une chaine de caractères à un modèle se fait selon la syntaxe
(chaine =~ /modèle/) pour tester la correspondance au modèle (chaine !~ /modèle/) pour tester la non-correspondance au modèle Pour définir un modèle on peut utiliser les symboles suivants :
^ indique le début du modèle $ indique la fin du modèle . caractère quelconque * répétition 0 fois ou plus du caractère qui précède ? répétition 0 ou 1 fois du caractère qui précède + répétition 1 fois ou plus du caractère qui précède {a,b} répétition de a fois à b fois exactement \ empêche l'interprétation du caractère qui suit s'il est spécial ( ^ $ . ? etc..) [..] classe de caractères admissibles (..|..) alternative On peut également utiliser les séquences :
\s les caractères du style "espace" (comme tabulation, retour-charriot...) \w un caractère alphanumérique (ou mot "d'identificateur") \d un caractère numérique Les références arrières notées \1, \2... et les variables spéciales $1, $2... correspondent aux parties de chaines correspondant aux expressions régulières spécifiées. L'utilisation des expressions régulières se fait principalement par les syntaxes
$variable =~ m/expression/remplacement/ [egimosx] ; $variable =~ s/expression/remplacement/ [egimosx] ; $variable =~ tr/expression/remplacement/ [cds] ;
Par exemple$phr =~ tr/A-Z/a-z/ ;
convertit en minuscules (en mode américain) la phrase contenue dans $phr.Pour plus de détails, voir la voir la référence correspondante en ligne ou le chapitre 6 du cours de F. Dagorn.
FICHIERS
L'ouverture se fait avec open :
- ouverture d'un fichier en lecture uniquementopen ( descripteur,"nom_fichier");
- ouverture en écriture avec écrasement en cas d'existence préalable du fichieropen ( descripteur,"> nom_fichier");
- ouverture en écriture avec ajout en fin de fichieropen ( descripteur,">> nom_fichier");
Les autres opérations sur un fichier sont :
- écriture dans un fichier ouvertPour tester la présence, les attributs d'un fichier, voir la page de référence correspondante en ligne.print descripteur "ce qu'il y a à ajouter";
- lecture dans un fichier : elle se fait par lignes$ligne = < descripteur > ;
- fermeture d'un fichierclose descripteur ;
- destruction d'un ou plusieurs fichiersunlink (liste des noms de fichiers);
TABLEAUX
Il y a deux sortes de tableaux :
- les tableaux classiques (indicés, en liste...) notés @tab
- les tableaux associatifs ("hachages") notés %tab.L'expression $#tab donne l'indice du dernier élément d'un tableau classique. Le premier indice du tableau est 0. Le nombre d'éléments d'un tableau est donc $#tab+1
L'affectation d'un tableau suit les syntaxes (suivant les cas)
@tabC = ( liste des éléments ); %tabH = ( cle1,élément1,clé2,élément2, etc.. );
Remarque : pour un tableau associatif, on peut aussi utiliser la syntaxe%tabH = ( cle1 => élément1, clé2 => élément2, etc.. );
Voici des exemples d'utilisation@mois_let_C = ("jan","fev","mars",...) ; %mois_num_H = ("jan",1,"fev",2,......) ; %mois_num_H = ("jan"=>1, "fev"=>2,......) ;
L'extraction d'un élément d'un tableau se fait suivant les cas par$tabC[$i] # c'est la i-ième élément de tabC $tabH{$i} # c'est la valeur de la i-ième clé de tabH
On notera que keys %tabH donne la liste des clés de hachage et que values %tabH donne la liste des valeurs associées aux clés de hachage. Les instructions sort et reverse permettent d'influer sur l'ordre des ces listes. L'article "Tris en Perl" de J. Quelin détaille comment effectuer ces tris.
Le parcours des éléments d'un tableau se fait par
foreach $var (@tab) { # on traite $var } foreach $cle (keys %tab) { # on traite $cle et $tab{$clé} }
La transformation d'un tableau en une chaine de caractères utilise la fonction join selon la syntaxe
$chaine = join($separateur,@tableau)
Et la transformation (inverse) d'une chaine de caractères en un tableau utilise la fonction split selon la syntaxe
@tableau = split($separateur,$chaine)
Les listes explicites parenthésées peuvent être considérées comme des tableaux classiques. Ainsi l'instruction($a,$b) = ($b,$a)
permute le contenu des deux variables $a et $b alors que l'instruction
($tvar[$nbve],$tval[$nbve]) = split(/=/,$ligne) ;
réalise une affectation multiple : la première variable (qui est en fait un élément de tableau) reçoit la partie de la variable $ligne qui est avant le caractère "=" et la deuxième variable ce qui est après.
SOUS-PROGRAMMES
La définition d'un sous-programme se fait avec l'instruction sub selon la syntaxe
sub nomSousProgramme { ... return ($res1,...,$resp) ; # renvoi éventuel des résultats } ; # fin du sous-programme
La récupération des paramètres effectifs s'effectue parlocal ($var1, ... ,$varn) = @_;
et la déclaration des variables locales éventuelles parlocal $VAR1,...,$VARm ;
ou parmy $VAR1,...,$VARm ;
L'appel d'un sous-programme se fait directement soit par
sousProgs() ;
ou par
sousProgs(($param1,$param2...)) ;
s'il y a des paramètres. Lorsque le sous-programme renvoie des valeurs, on peut les récupérer en liste via l'instruction :
($res1,$res2...) = &nom_ss_prog($param1,$param2...) ;
Il est conseillé de mettre un é-commercial ou "ampersand" devant le nom des fonctions que l'on définit soi-même comme par exemple
&gh_copyright() ;
plutot quegh_copyright() ;
MODULES, OBJETS ET BIBLIOTHEQUES DE SOUS-PROGRAMMES
On utilise un module (fichier de type .pm) à l'aide de use ; les sous-modules sont alors précisés par le symbole : et correspondent aux sous-répertoires physiques de l'implémentation des fichiers .pm. On peut forcer à vérifier un numéro de version à utiliser via require. L'instruction bless permet de définir des classes d'objets. La syntaxe ${EXPR}->([LIST]) donne alors accès aux méthodes et attributs des objets.
La notation -> peut aussi s'appliquer aux tableaux. Ainsi $tC->[28] est le même élément que $tC[28] et $tH->{28} est le même élément que $tH{"28"}
Par exemple pour utiliser le fichier Xml/Parser.pm correctement installé, puis pour créer un objet $pgh et en utiliser la méthode parsefile on peut écrire :
use XML::Parser; ... my $ghp= new XML::Parser( Style => 'Stream'); ... $ghp->parsefile( $nf ) ;
La définition d'un module se fait avec package. On pourra consulter les chapitres 14 et 15 du cours de F. Dagorn pour comment cela fonctionne en détail. Nous fournissons à titre d'exemple notre module strFuncs.pm qui contient des fonctions utiles sur chaines de caractères pour Perl, certaines n'étant que des appels lisibles aux fonctions et variables "égyptiennes" de Perl.
OPTIONS DE LA COMMANDE PERL
Si on tape perl -v on obtient une description courte de la version de Perl utilisée, alors que si on tape perl -V on obtient une description longue de la version de Perl utilisée.
Si on tape perl -c nom_prog perl teste seulement si le programme est syntaxiquement correct.
Si on tape perl -d nom_prog perl exécute le programme en mode Debug.
Si on tape perl -e 'expression|s]' alors perl exécute l'expression comme s'il s'agissait d'un programme. Par exemple
perl -e ' $a = "le chien" ; print substr($a,4,2)."\n" ; '
affiche la chaine en. On appelle cela du "one-liner" en anglais. Certains sites sont spécialisés en de telles expressions comme par exemple : en français et sial.
Attention, cela est parfois totalement illissible, comme
perl -lne'/^(\S+).*?"(.*?)"/;length$h{$1}>length$2or$h{$1}=$2} {print"@a"while@a=each%h' access.log
et pourtant cela fonctionnne. Voir les explications à l'URL
http://linuxgazette.net/issue90/okopnik.html. Signalons deux "one-liner" très utiles :
perl -p -e 's/original/replacement/g' FICHIERS* perl -p -i .bak -e 's/original/replacement/g' FICHIERS*
ces deux lignes permettent de remplacer une chaine par une autre dans une série de fichiers (par exemple de remplacer "Paris, 2004" par "Angers, 2005" dans les fichiers *.htm) ; la deuxième commande crée une copie en .bak des fichiers originaux.
Pour plus de détails sur les arguments de la commande Perl, vous pouvez consulter la page française
http://www.ftls.org/fr/initiation/perl/index3.shtml Terminons par une option intéressante en ligne de commande, celle qui permet d'installer un module connaissant son nom. Si on veut installer par exemple (en tant que "root" car il faut avoir des droits d'écriture sur le système) le module Schema décrit au CPAN comme XML::Validator::Schema, on exécute la commande
perl -MCPAN -e 'install XML::Validator::Schema'
On voit alors à l'écran la connection puis la copie et la configuration et l'installation s'effectuer. Il ne reste plus alors qu'à indiquer aux utilisateurs qu'ils peuvent désormais mettre
use XML::Validator::Schema
dans leurs programmes Perl...
EXEMPLES CLASSIQUES DE PROGRAMMATION EN PERL
Bonjour
Ce programme efface l'écran (sous Unix), demande un prénom et dit au revoir après avoir donné la date et l'heure (mois en français) et après avoir converti le prénom en majuscules.
#!/usr/bin/perl system "clear"; print " Bonjour.\n Quel est votre prénom ? "; chop ($pren = <STDIN>); @date = gmtime(time); print"\n"; @mois = ("janvier","février","mars","avril","mai","juin", "juillet","aout","septembre","octobre","novembre","décembre"); $an = 1900 + $date[5] ; $phr = " Le $date[3] $mois[$date[4]] $an " ; $phr .= "à $date[2]:$date[1]:$date[0], au revoir, \U$pren\E."."\n\n"; print $phr ;
Vous pouvez utiliser le programme bonjour.pl correspondant.Voici un exemple d'exécution (l'entrée-clavier est en bleu) :
Bonjour. Quel est votre prénom ? gh Le 6 janvier 2005 à 9:27:21, au revoir, GH.
Table de Multiplication
Le programme attend un paramètre. Si on ne lui en passe pas, le programme affiche la syntaxe d'appel. Si le paramètre n'est pas un entier positif, il redemande une valeur entière et positive.
Enfin, lorsque le nombre est entier, on afiche sa table de multiplication avec cadrage des unités sous les unités, des dizaines sous les dizaines etc.
# test du passage de paramètre if ($#ARGV == -1) { print stderr "\n syntaxe : tabmult nombre_entier_positif\n\n" ; exit(-1) ; } ; # fin de si # affectation du paramètre $nbChoisi = $ARGV[0] ; # relance éventuelle while ( not entier( $nbChoisi ) ) { print " nombre incorrect. Redonner un nombre ENTIER "."\n" ; chomp($nbChoisi=<STDIN>) ; } ; # nombre invalide # boucle d'affichage print " Table de " ." ". $nbChoisi."\n" ; for ( $indb = 1 ; $indb <= 10 ; $indb++ ) { $produit = $nbChoisi * $indb ; $find = &format( $indb , 2 , 0 ) ; $fpro = &format( $produit , 5 , 0 ) ; print $find ." ". " fois " ." ". $nbChoisi ." ". " = " ." ". $fpro."\n" ; } ; # indb de1a 10 ##### sous-programmes : sub entier { # renvoie "vrai" si le paramètre est un entier éventuellement signé my $parm_entier = $_[0] ; return $parm_entier =~ /^[+-]?\d+$/ ; } ; # fin sub entier sub format { # formate un nombre (syntaxe plus simple que sprintf) return( sprintf("%$_[1]d",$_[0]) ) ; } ; # fin sub formatVous pouvez utiliser le programme tabmult.pl correspondant.
Voici un exemple d'exécution (les entrées-clavier sont en bleu) :
perl tabmult.pl syntaxe : tabmult nombre_entier_positif perl tabmult.pl ok nombre incorrect. Redonner un nombre ENTIER 3+2 nombre incorrect. Redonner un nombre ENTIER 5 Table de 5 1 fois 5 = 5 2 fois 5 = 10 3 fois 5 = 15 4 fois 5 = 20 5 fois 5 = 25 6 fois 5 = 30 7 fois 5 = 35 8 fois 5 = 40 9 fois 5 = 45 10 fois 5 = 50Factorielle
Il s'agit du classique calcul de la factorielle d'un entier. On le présente ici pour montrer la récursivité en Perl.
# on peut aussi écrire le sous-programme AVANT le programme principal sub fact { local ($ind )= @_ ; # transmission des paramètres local $res,$indmun ; if ( $ind == 0 ) { $res = 1; } else { $indmun = $ind-1; $res = &fact ($indmun) * $ind; } ; # fin de si return $res ; # renvoi de la valeur } ; # fin de sub fact ############ programme principal print "Donnez le nombre positif dont vous voulez la factorielle : " ; chop ($nb = <STDIN>) ; print "\nLa factorielle de ",$nb," est ",&fact( $nb),"\n\n" ; exit(12345) ; # sortie avec code-retour expliciteVous pouvez utiliser le programme factorielle.pl correspondant.
Voici un exemple d'exécution (l'entrée clavier est en bleu) :
Donnez le nombre positif dont vous voulez la factorielle : 5 la factorielle de 5 est 120.
Moyenne des notes et tris
Le but du programme est de lire un fichier de noms et de notes. Il y a un nom et 3 notes par ligne (quand elle n'est pas vide). Il y a une étoile pour séparer le nom des notes. On veut un affichage par ordre alphabétique et par moyenne décroissante (ordre de "mérite"). On utilise bien sur des tableaux associatifs.
# on recupère le fichier des noms et des notes passé en argument if ($ARGV[0] eq "") { print "\n la syntaxe est moynotes nom_de_fichier.\n\n" ; exit(-1) ; } ; # fin de test sur les arguments $fiche = $ARGV[0] ; # récupération du nom du fichier open( FIC ,"<$fiche") || die "\n impossible d'ouvrir le fichier nommé $fiche \n\n" ; # parcours du fichier : on crée un tableau associatif dont # les clés sont les noms associés a la moyenne $nbElv = 0 ; while ($ligne=<FIC>) { @champs=split(/\*/,$ligne) ; # on supprime le retour chariot de la fin de la ligne dans champs[1] chomp ($champs[1]) ; # si la ligne est vide, on réitère la boucle if (length($champs[1])==0) { next ; } ; # on sépare les 3 notes @notes=split(/\s+/,$champs[1]) ; # on supprime la 1ère valeur des champs qui est un espace shift @notes ; $som=$notes[0]+$notes[1]+$notes[2] ; $moy=$som/3 ; $classe{$champs[0]}=$moy ; # on incrémente le compteur d'élèves $nbElv++ ; } ; # fin de tant_que print "Il y a ",$nbElv," élèves." ; # on trie la liste des élèves par ordre alphabétique print " Voici la liste des élèves par ordre alphabetique:\n" ; foreach $name (sort keys %classe) { printf " %s %10.2f\n",$name,$classe{$name} ; } ; # fin pour chaque # on trie la liste par ordre de mérite print "Voici la liste des élèves par ordre de mérite:\n" ; foreach $nom (reverse (sort by_value keys %classe)) { printf " %s %10.2f\n",$nom,$classe{$nom} ; } ; # fin pour chaque sub by_value { local($test_num)=$classe{$a} <=> $classe{$b} ; # si $classe{$a}=$classe{$b} les deux moyennes sont identiques, # donc il faut trier par ordre alphabétique les noms # si les deux sont égaux, $test_num vaut 0, donc on trie selon la # clé. Sinon on ne fait rien. if ($test_num) {return $test_num ;} return $b cmp $a ; } ; # fin sub by_valueVous pouvez utiliser le programme moynotes.pl correspondant.
Voici un exemple d'exécution (les entrées-clavier sont en bleu) :
tuteurs>cat examen.notes BOBY * 18 5 5 <-- ici une ligne vide ZELYNOU * 6 11 7 ANTIN * 8 4 10 BOB * 16 8 15 IZEL * 16 18 12 tuteurs>perl moynotes.pl la syntaxe est moynotes nom_de_fichier. tuteurs>perl moynotes.pl exam.not impossible d'ouvrir le fichier nommé exam.not tuteurs>perl moynotes.pl examen.notes Il y a 5 élèves. Voici la liste des élèves par ordre alphabétique: ANTIN 7.33 BOB 13.00 BOBY 9.33 IZEL 15.33 ZELYNOU 8.00 Voici la liste des élèves par ordre de mérite: IZEL 15.33 BOB 13.00 BOBY 9.33 ZELYNOU 8.00 ANTIN 7.33EXEMPLES MOINS TRIVIAUX EN PERL
Conversion des noms de fichiers en minuscule
Ce programme met en minuscule le nom des fichiers passés en paramètre. On utilise tr pour la conversion majuscule/minuscule et la fonction rename qui "cause" au système d'exploitation.
# minu.prl : met le nom du ou des fichier(s) en minuscule $nbf = @ARGV ; # nombre de fichiers à traiter if ($nbf==0) { print " minu.pl : met le nom du ou des fichier(s) en minuscule \n" ; print " syntaxe : minu nom(s)_de_fichier(s) \n" ; print " exemples : minu ESSAI \n" ; print " minu A* \n" ; } else { foreach $oldfid (@ARGV) { $newfid = $oldfid ; $newfid =~ tr/A-Z/a-z/ ; if ($newfid ne $oldfid) { rename($oldfid,$newfid) } ; } ; # fin de pour chaque print " $nbf fichier(s) converti(s)." ; } ; # fin de si nbf n'est pas nulVous pouvez utiliser le programme minu.pl correspondant.
Voici un exemple d'exécution (les entrées-clavier sont en bleu) :
demoPerl>perl minu.pl minu.pl : met le nom du ou des fichier(s) en minuscule syntaxe : minu nom(s)_de_fichier(s) exemples : minu ESSAI minu A* demoPerl>ls Essai.ALG Essai_C.ALG essai_c.cpp essai_c.lvm Essai_J.ALG essai.c essai_c.c essai_c.lst essai.cpp essai_j.class demoPerl>perl ~/public_html/tuteurs/minu.pl *.ALG 3 fichier(s) converti(s). demoPerl>ls essai.alg essai_c.alg essai_c.cpp essai_c.lvm essai_j.alg essai.c essai_c.c essai_c.lst essai.cpp essai_j.classLois probabilistes (cgi)
Ce programme effectue des calculs statistiques (on peut ignorer le détail des calculs) pour la page Web forge.info.univ-angers.fr/~gh/vitrine/Democgi/loisStatp.htm. Le formulaire utilise une méthode "POST". Nous n'en donnons pas tout le détail du programme ici car il est long. Nous n'en présentons que le début
#! /usr/bin/perl print "Content-type: text/html\n" ; print "\n" ; print '<HTML>' ; print '<HEAD>' ; print "<TITLE> Lois statistiques usuelles discrètes </TITLE>" ; print '</HEAD>' ; print '<BODY bgcolor="#ffffff">' ; print '<font size="6" color ="black"><b> Lois statistiques usuelles discrètes</b></font><p>' ; print '<blockquote>' ; print '<font size="5" color ="blue"><b>' ; print '<pre>' ; # récupération de la variable d'environnement $qs = $ENV{"QUERY_STRING"} ; # décodage de la chaine $lig = "" ; for ($idc = 0 ; $idc < length($qs) ; $idc++) { $c = substr($qs,$idc,1) ; if ($c eq "=") { $lig = "" ; $c = " " } if ($c eq "+") { $c = " " ; } ; $lig = "$lig$c" ; } ; # fin pour # appel du sous-programme de calcul &lois(split(" ",$lig)) ; print '</pre>' ; print '</b>' ; print '</font>' ; print '</blockquote>' ; print '</BODY>' ; print '</HTML>' ; exit(0) ; ################################################## # (gH) -- lois.pl ; TimeStamp (unix) : 09 Janvier 01 11:45 sub syntaxe { $ligSyntax = " [ b p | B n p | G p t | H n k s | P m t | Q s p t \n" ; $ligSyntax .= (" "x18)." | T a | U a b | S m t | Z ] T " ; print "\n" ; print " syntaxe : lois $ligSyntax\n\n" ; print " exemples : \n" ; print " lois b 0.2 # loi de bernoulli p=0.2\n" ; print " lois B 5 0.1 # loi binomiale n=5, p=0.1\n" ; print " lois G 0.8 9 # loi géométrique p=0.8 limitée à 9 termes\n" ; print " lois H 10 5 3 # loi hypergéométrique N=10, K=5, n=3\n" ; print " lois N 4 0.9 11 # loi de pascal s=4, p=0.9 (11 termes)\n" ; print " lois P 1.2 9 # loi de poisson m=1.2 limitée à 9 termes\n" ; print " lois S 0.3 9 # loi \"sans-nom\" p=0.3 limitée à 9 termes\n" ; print " lois T 4 # loi triangulaire a=4\n" ; print " lois U 2 7 # loi uniforme a=2, b=7\n" ; print " lois Z # loi de zipf-benford \n" ; print "\n T est la taille de l'échantillon correspondant (100 par défaut)." ; print "\n" ; } ; # fin sub syntaxe sub lois { ########################################################################## ## ## ## sous-programme principal ## ## ## ########################################################################## print "\n" ; print " (gH) ; lois.pl v1.2 : lois statistiques discrètes usuelles \n" ; @gARGV = @_ ; if ($#gARGV==-1) { &syntaxe() ; exit 0 ; } ; $nomLoi{"b"} = "de bernoulli" ; $nomLoi{"B"} = "binomiale (compte-avec)" ; $nomLoi{"G"} = "géométrique (bernoulli négative)" ; $nomLoi{"H"} = "hypergéométrique (compte-sans)" ; $nomLoi{"N"} = "de pascal (binomiale négative ou première-avec)" ; $nomLoi{"P"} = "de poisson" ; $nomLoi{"S"} = "sans-nom (première-sans)" ; $nomLoi{"U"} = "uniforme discrète" ; $nomLoi{"T"} = "triangulaire" ; $nomLoi{"Z"} = "de zipf-benford" ; if ($gARGV[0] eq "b") { &bernoulli($gARGV[1],$gARGV[2]) ; exit 0 ; } ; if ($gARGV[0] eq "B") { &binomiale($gARGV[1],$gARGV[2],$gARGV[3]) ; exit 0 ; } ; if ($gARGV[0] eq "G") { &geometrique($gARGV[1],$gARGV[2],$gARGV[3]) ; exit 0 ; } ; if ($gARGV[0] eq "T") { &triangulaire($gARGV[1],$gARGV[2]) ; exit 0 ; } ; if ($gARGV[0] eq "U") { &uniforme($gARGV[1],$gARGV[2],$gARGV[3]) ; exit 0 ; } ; if ($gARGV[0] eq "Z") { &zipfbenford($gARGV[1]) ; exit 0 ; } ; print "\n loi $ARGV[0] : pas encore implémentée\n ou faute de frappe sur l'initiale de la loi !\n" ; exit 0 ; ######################################################################### sub log10 { return log($_[0])/log(10) ; } ; ######################################################################### sub cnk { # coefficients du binome my ($n,$p) = ($_[0],$_[1]) ; if ($p>$n) { return 0 ; } ; if ($n<0) { return 0 ; } ; if ($p<0) { return 0 ; } ; $k = $p ; if ($k>$n-$k) { $k = $n-$k ; } ; if ($k==0) { $res = 1 ; } elsif ($k==1) { $res = $n ; } elsif ($k==2) { $res = $n*($n-1)/2 ; } else { $i = 0.0 ; $frac = 1.0 ; while ($i<$p) { $i++ ; $frac = $frac * ($n+1-$i)/$i ; } ; # fin tant que $res = $frac ; } ; # fin de si return $res ; } ; # fin sub cnk ######################################################################### sub bernoulli { $proba = $_[0] ; $taille = $_[1] ; $nbVal = 2 ; $paramLoi = "p=$proba" ; ($tabVal[1],$tabVal[2]) = (0,1) ; ($tabPro[1],$tabPro[2]) = (1-$proba,$proba) ; &decritLoi("b") ; } ; # fin sub bernoulli ... ICI Il MANQUE (volontairement) LA FIN DU PROGRAMME ##################################### #####################################Vous pouvez utiliser le programme lois.pl correspondant. Pour l'exécution, allez sur la page forge.info.univ-angers.fr/~gh/vitrine/Democgi/loisStatp.htm.
Expressions régulières : analyse de fichiers-logs
On s'intéresse ici au fichier authlog.txt dont voici le début :
Jul 26 20:06:34 deneb PAM_unix[15771]: (system-auth) session opened for user brejeon by (uid=0) Jul 26 20:06:34 deneb -- brejeon[15771]: LOGIN ON pts/5 BY brejeon FROM sirius Jul 26 20:43:32 deneb PAM_unix[15771]: (system-auth) session closed for user brejeon Jul 27 01:24:53 deneb PAM_unix[15960]: (system-auth) session opened for user guilleme by (uid=0) Jul 27 01:24:53 deneb -- guilleme[15960]: LOGIN ON pts/5 BY guilleme FROM sirius Jul 27 01:29:29 deneb PAM_unix[15960]: (system-auth) session closed for user guilleme Jul 27 01:30:33 deneb PAM_unix[15996]: check pass; user unknown Jul 27 01:30:33 deneb PAM_unix[15996]: authentication failure; (uid=0) -> etudinfo for system-auth service Jul 27 01:30:33 deneb login[15996]: FAILED LOGIN 1 FROM sirius FOR etudinfo, Authentication failure ...On profite des expressions régulières et des tableaux généraux de perl pour compter rapidement le nombre de sessions ouvertes et fermées, le nombre d'utilisateurs. Il est conseillé de lire et de relire expresreg.htm avant de tenter la compréhension du script prl :
# (gH) -- analog.pl ; TimeStamp (unix) : 11 Février 2005 vers 16:56 print "\n (gH) Exemple d'analyse de logs\n\n" ; # on met tout dans la variable src while (<>) { $src .= $_ } ; # transfert dans la variable "bien connue de perl" $_ = $src ; # pour vérifier qu'on a tout lu : print "\n\n$src\n\n" ; ################################################################### # on cherche les ouvertures : le ? sert à modérer l'expression régulière $so = 0 ; while (m| session opened for user (.*?) by |gs) { $so++; if ($so<5) { print " " .sprintf("%4d",$so)." $1\n"; } elsif ($so==5) { print " ...\n\n" ; } ; } ; # fin de tant que print " ".sprintf("%4d",$so)." sessions ouvertes\n" ; # on cherche les fermetures : le ? sert à modérer # pas besoin de $sc = 0 ; car perl s'en charge. while (m| session closed for (.*?) by |gs) { $sc++; } ; # fin de tant que print " ".sprintf("%4d",$sc)." sessions fermées\n" ; ################################################################### # on recommence en plus aggressif avec les deux chaines # à la fois dans une liste print "\n Rebelote : \n\n" ; @ach = ("session opened for user","session closed for") ; foreach $mac (@ach) { while (m|$mac (.*?) by |gs) { $nbs{$mac}++ ; } ; } ; # fin de pour chaque foreach $mac (reverse sort @ach) { print " ".sprintf("%-30s %6d",$mac, $nbs{$mac})."\n" ; } ; # fin de pour chaque ################################################################### print "\nRécupération des utilisateurs\n" ; $nu = 0 ; foreach $mac (@ach) { while (m|$mac (.*?) by |gs) { $nu++ ; $usr{$1}++ ; # pour debug : if ($nu<5) { print " ".sprintf(" %5d %-15s %-25s", $nu, $1, $mac)."\n" ; } elsif ($nu==5) { print " ...\n" ; } ; } ; # fin de tant que } ; # fin de pour chaque print "\n Num Utilisateurs Nb Sessions (o/c) \n" ; $nu = 0 ; foreach $uti (sort keys %usr) { $nu++ ; if ($nu<10) { print " ".sprintf(" %5d %-30s %6d", $nu, $uti, $usr{$uti})."\n" ; } elsif ($nu==10) { print " ...\n" ; } ; } ; # fin de pour chaque # fin de l'exemple print "\n (gH) -- fin d'analyse de logs\n\n" ;Voici les résultats fournis par le perl nommé analog.pl:
(gH) Exemple d'analyse de logs 1 brejeon 2 guilleme 3 guilleme 4 roynette ... 152 sessions ouvertes 74 sessions fermées Rebelote : session opened for user 152 session closed for 74 Récupération des utilisateurs 1 brejeon session opened for user 2 guilleme session opened for user 3 guilleme session opened for user 4 roynette session opened for user ... Num Utilisateurs Nb Sessions (o/c) 1 alaing 3 2 alexp 1 3 anthonyp 3 4 antier 1 5 blouin 1 6 bodi 1 7 brault 1 8 brejeon 6 9 buraud 1 ... (gH) -- fin d'analyse de logs
Extractions d'adresses dans les pages Web
On veut extraire (sans doublon) tous les liens de la page afine.html. Pour cela on utilise le programme anapw.pl que voici :
# (gH) -- anapw.pl ; TimeStamp (unix) : 11 Février 2005 vers 18:24 while (<>) { $src .= $_ } ; $_ = $src ; # question pour les étudiants et pour EJ # pourquoi le troisième .* a -t-il un ? mais pas de () while (m|<a href="(.*?)".*?>(.*?)</a>|gs) { if (++$nom{$2}==1) { print " ".sprintf("%-30s %-30s",$2, $1)."\n" ; } ; } ; # fin tant que # que voit-on avec : if ($nom{$2}++==1) { print ...Voici un extrait de ce qu'il affiche :
... Bacillus alcalophilus http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Bacillus+alcalophilus 1BKS http://www.rcsb.org/pdb/cgi/explore.cgi?pdbId=1BKS Salmonella typhimurium http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Salmonella+typhimurium 1RNH http://www.rcsb.org/pdb/cgi/explore.cgi?pdbId=1RNH 1RHD http://www.rcsb.org/pdb/cgi/explore.cgi?pdbId=1RHD 1PRC http://www.rcsb.org/pdb/cgi/explore.cgi?pdbId=1PRC Blastochloris viridis http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Blastochloris+viridis 1PHH http://www.rcsb.org/pdb/cgi/explore.cgi?pdbId=1PHH Pseudomonas fluorescens http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Pseudomonas+fluorescens 1HIP http://www.rcsb.org/pdb/cgi/explore.cgi?pdbId=1HIP Chromatium vinosum http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?name=Chromatium+vinosum 1GP1 http://www.rcsb.org/pdb/cgi/explore.cgi?pdbId=1GP1 4GCR http://www.rcsb.org/pdb/cgi/explore.cgi?pdbId=4GCR ...
Dans le même genre d'idées, on pourra consulter adf.pl qui affiche tous les <font .... d'une page Web.Formulaire perl-cgi
Dans la mesure où il existe des modules perl pour écrire des formulaires, afficher un panneau de saisie et tester la réponse utilisateur est un jeu d'enfant. Voici par exemple un exemple de script cgi nommé gereform.pl qui utilise le module CGI et qui demand un nom et un prénom.
Cet exemple, minimal, est typique d'un script "tout-en-un" qui selon la valeur de ses variables, affiche le formulaire, le teste ou l'exécute après l'avoir validé.
#!/usr/bin/perl -w use CGI ; $mp = new CGI ; print $mp->header(), $mp->start_html(-title=>"Essai de Formulaire") ; if ($mp->param()) { &traitement() ; } else { &saisie() ; } ; print $mp->end_html() ; ############ fin du programme principal sub saisie { print "<table>" ; print $mp->startform() ; print "<tr><td>Nom </td>" ; print "<td>".$mp->textfield(-name=>"Nom")."</td></tr>" ; print "<tr><td>Prenom </td>" ; print "<td>".$mp->textfield(-name=>"PreNom")."</td></tr>" ; print "<tr><td></td><td>".$mp->submit(-value=>"Ok")."</td></tr>" ; print "</table>" ; print $mp->endform() ; } ; # fin saisie ########################### sub traitement { print "<h1> Traitement des infos</h1>" ; print "Votre nom est <font size=\"+2\" color=\"blue\">".$mp->param("Nom")."</font>"; } ; # fin traitementEXERCICES D'ENTRAÎNEMENT (solution)
E1. numéroter les lignes d'un fichier (solution)
On veut ici numéroter proprement les lignes d'un fichier avec un numéro de ligne sur 4 caractères avec des 0 non significatifs éventuels. Par exemple, pour le fichier fic.xmp qui contient
Ligne 1 (vide) ceci est la fin (ligne 3 ?).on voudrait obtenir
0001 Ligne 1 0002 (vide) 0003 ceci est la fin (ligne 3 ?).Ecrire une solution classique puis une solution concise "à la perl".
E2. variables d'environnement (solution)
Les variables d'environnement (commande set sous Dos comme sous Unix) sont des couples (variable, valeur). On voudrait les afficher en tableau avec deux colonnes bien séparées, comme par exemple
Variable Valeur OPTERR 1 SHELL /bin/bash TERM XtermEcrire une solution avec des tableaux classiques puis avec des tables de hachage. Reprendre ensuite sous forme de cgi en rajoutant de la couleur dans l'affichage classique de set, comme le fait la page
http://forge.info.univ-angers.fr/scripts/env.pl E3. dictionnaires d'un texte (solution)
Ecrire un programme Perl qui lit un texte passé en paramètre et en fait les dictionnaires alphabétiques et par occurence. Voir la page
forge.info.univ-angers.fr/~gh/Wanalexies/f_dicos.php comme exemples de texte et de résultats.
On pourra utiliser le fichier debcand.txt qui contient le début du texte de Candide de Voltaire pour tester le programme. On devrait obtenir :
Analyse du fichier debcand.txt : 204 ligne(s), 1602 mot(s) dont 666 mot(s) différent(s). Vous pouvez consulter les fichiers dic_debcand.txt.mot et dic_debcand.txt.occ dont voici le début : fichier dic_debcand.txt.mot issu de dico.pl debcand.txt Abares 1 Ah 2 BEAU 1 BULGARES 1 Bulgares 4 C 1 CANDIDE 2 CE 1 CHAPITRE 2 CHASSé 1 CHâTEAU 1 COMMENT 2 Camarade 1 Candide 20 Comme 2 Comment 1 Cunégonde 9 D 1 DANS 1 fichier dic_debcand.txt.occ issu de dico.pl debcand.txt de 55 le 52 et 46 la 39 les 28 il 25 des 23 un 22 Candide 20E4. calcul de Chi2 (solution)
Un utilisateur me demande d'installer un module perl pour afficher les valeurs qu'on trouve dans une table de chi2. Il sait que ce module est Distributions.pm et me dit qu'on doit utiliser la fonction chisqrdistr. Pour m'aider il me dit qu'avec les deux paramètres 4 et 5 on doit trouver 9.4877 alors qu'avec les valeurs 17 et 5 on doit obtenir 27.587.
Expliquer comment on installe ce module en tant que root pour tout le monde et en tant que simple utilisateur dans un répertoire personnel.
E5. fichiers DBF (solution)
Un utilisateur qui utilise beaucoup de petites bases de données (en fait des tableaux rectangulaires avec des colonnes typées et avec des lignes qui ont toutes le même format) a besoin de lire en perl des fichiers Dbase, c'est à dire des fichiers de type DBF.
Justifier le choix de Dbase (et plus généralement Xbase) par rapport au choix d'Excel ou d'un tableur quelconque pour de tels tableaux. Sachant que le module perl correspondant est XBase.pm (et ses fichiers), essayer d'afficher le nombre de lignes du tableau et les données du tableau vins.dbf (on séparera les colonnes par un espace sans se préoccuper du formatage) ; on trouvera plus de renseignements sur le dossier dans la page vins.htm.
Modifier le programme pour qu'il affiche les données dans une page Web.
E6. extractions d'adresses (solution)
Reprendre le programme d'extractions d'adresses et répondre aux questions du texte du programme mises en commentaire.
Modifier le programme pour qu'il affiche les adresses par ordre alphabétique de référence puis par ordre alphabétique de site.
E7. que fait ce programme ? (solution)
Après avoir lu soigneusement le programme ldphp.pl essayer de l'utiliser, par exemple en lui passant comme paramètre le fichier std.php (il vous faudra sans doute aussi le fichier strfun.php).
Cela peut-il vous être utile ?