Programmation Perl/Version imprimable
Une version à jour et éditable de ce livre est disponible sur Wikilivres,
une bibliothèque de livres pédagogiques, à l'URL :
https://fr.wikibooks.org/wiki/Programmation_Perl
Introduction
Perl (Practical Extraction and Report Language ou langage pratique d'extraction et de génération de rapports) est un langage de programmation créé par Larry Wall en 1987 et reprenant des fonctionnalités du langage C et des langages de scripts sed, awk et shell (sh). Bien que son nom soit l'objet de plusieurs acronymes on l'utilise généralement avec un P majuscule pour désigner le langage et un p minuscule en parlant de l'interpréteur. « Seul perl analyse correctement Perl ».
Origines et implémentation
[modifier | modifier le wikicode]Perl est né du besoin de disposer d'un langage optimisé pour l'extraction d'informations de fichiers textes et la génération de rapports.
Une de ses devises est « There Is More Than One Way To Do It (TIMTOWTDI) » qui pourrait se traduire par « Il y a plus d'une façon de le faire ». Ce qui est un point fort d'après les adeptes puisque chacun peut écrire du code comme il l'entend. Bien entendu, ceci peut être vu comme un point faible puisque ça donne lieu à différents styles très différents de code et pour le lecteur qui lira des codes de différents programmeurs, il faut s'adapter.
Une autre est « Perl : le couteau suisse des langages de programmation ». Sa souplesse autorise l'emploi de plusieurs modèles de programmation : programmation procédurale, programmation fonctionnelle et POO---bien que les puristes de ce dernier ne considèrent pas Perl comme en faisant partie, son objectif est de s'adapter aux goûts du programmeur plutôt qu'une architecture stricte du langage. Perl est souvent considéré comme le langage de script par définition et a été qualifié de "ciment assurant la cohésion du web", étant un des langages CGI les plus populaires.
Un autre point qui ne fait pas consensus est le nombre de raccourcis dans le langage Perl pour écrire du code plus rapidement et de façon élégante. Mais l'usage trop courant de ces raccourcis rend le code plus difficile à appréhender pour un non averti.
Perl est un logiciel libre, distribué sous licence artistique et GPL. Perl est porté sur la plupart des systèmes d'exploitation mais excelle particulièrement en environnement UNIX et ses dérivés; il gagne aussi en popularité sous Microsoft Windows. Un exemple du champ d'action de Perl est son utilisation comme script CGI pour faire tourner Wikipédia jusqu'en janvier 2002.
Une immense collection de modules Perl d'utilisation libre, allant des mathématiques avancés aux connexions aux bases de données, en passant par les réseaux et bien davantage encore, peuvent être téléchargés depuis un réseau de sites appelé CPAN.
Bien que Perl profite de la plupart des facilités d'un langage interprété, à proprement parler il n'interprète et n'exécute pas le code source une ligne à la fois. En fait, Perl compile d'abord le programme entier dans un bytecode intermédiaire (assez dans l'esprit du code objet Java), l'optimisant au passage, et exécute alors ce bytecode. Il est possible de compiler un programme Perl en bytecode pour s'épargner les phases de compilation lors d'exécutions futures, bien que l'« interpréteur » est toujours requis pour exécuter ce code.
Perl 6 est en cours de développement. Il tournera dans la machine virtuelle Parrot.
Ce qu'il faut savoir sur ce livre
[modifier | modifier le wikicode]Le but
[modifier | modifier le wikicode]Le but de ce livre est de fournir une documentation suffisante pour maîtriser en grande partie le langage Perl dans sa version 5 (à partir de la version 5.10). Il présentera la base du langage en premier lieu, puis quelques aspects avancés entrecoupés de tutoriels et travaux pratiques divers afin d'entraîner le lecteur à l'écriture de scripts divers. Le tout sera agrémenté d'un nombre conséquent d'exemples, de bonnes pratiques à adopter, d'indications sur les bibliothèques logicielles les plus utiles, et toute information qui nous semblera pertinente.
Les exemples présentés
[modifier | modifier le wikicode]Nous utiliserons la version 5.14 de l'interpréteur perl.
Tout code présenté ici commencera par ces 4 lignes qui seront présentées dans les bases du langage :
#!/usr/bin/env perl
use strict;
use warnings;
use v5.14;
Je ne les répèterai pas pour ne pas alourdir chaque exemple (qui seront nombreux), si vous souhaitez exécuter le code présent dans ce livre n'oubliez pas de les écrire en premier dans votre code. Cela fait partie des bonnes pratiques de programmation en Perl.
Pour les débutants
[modifier | modifier le wikicode]Vous voulez apprendre à programmer des applications, et ceci est votre premier livre sur le sujet. Voici quelques informations de base sur la programmation.
Votre premier éditeur de texte
[modifier | modifier le wikicode]Tout d'abord, pour programmer il faut un éditeur de texte. N'importe-quel éditeur de texte suffit pour programmer, cependant il est conseillé que votre éditeur de texte ait au moins la coloration syntaxique du langage que vous apprenez (ici Perl). Il en existe un nombre assez conséquent, mais voici quelques pistes :
- avec une interface graphique
- sans interface graphique (en ligne de commande sous GNU/Linux ou Unix)
Comment bien apprendre
[modifier | modifier le wikicode]Vous allez apprendre à programmer dans un certain langage de programmation. Avant de commencer, il faut savoir que cela va prendre du temps, et qu'il ne faut pas se précipiter. Lisez bien chaque exemple, essayer de modifier tous les bouts de code présentés en les complexifiant de plus en plus, essayez de faire tous les exercices et prenez le temps qu'il vous faut. Ce n'est pas une course, passez deux à trois heures maximum à apprendre par jour, c'est amplement suffisant.
Bases du langage
Bonjour !
[modifier | modifier le wikicode]L'un des plus petits programmes possible en langage Perl est :
#!/usr/bin/env perl
use strict;
use warnings;
use v5.14;
say "Bonjour !";
(Les numéros de lignes ont été ajoutés pour la lisibilité, mais ne font pas partie du programme.)
Ce programme a pour seul but d'afficher le texte « Bonjour ! » suivi d'un retour à la ligne. Voici la description de chaque ligne qui le compose :
#!/usr/bin/env perl
Ceci s'appelle un shebang et est destiné à déterminer quel est l'interpréteur à utiliser pour comprendre les instructions qui suivent.
use strict;
use warnings;
use v5.14;
Ceci décrit respectivement les conventions d'écriture (strict), l'affichage de message d'avertissement (warnings), ainsi que l'utilisation d'une certaine version de l'interpréteur Perl. Maintenant que vous savez ceci, vous comprenez que les 4 premières lignes ne sont pas toujours strictement nécessaires mais que cela fait partie des bonnes pratiques de les écrire. Pour cela, comme dit dans l'introduction, il faut inclure ces 4 lignes en début de chaque programme que vous écrirez.
say "Bonjour !";
Affiche simplement la chaîne de caractères Bonjour !. Le mot clé réservé say permet d'afficher une chaîne de caractères suivit par un retour à la ligne.
À noter qu'à l'exception de la première ligne, chaque autre ligne se termine par un point virgule, désignant comme dans beaucoup d'autres langages de programmation la fin d'une instruction.
Compilation
[modifier | modifier le wikicode]Pour pouvoir exécuter un programme Perl, il faut soit l'exécuter via la commande perl fichier_perl.pl
soit on rajoute le shebang (vu plus haut), on rend le fichier exécutable chmod +x fichier_perl.pl
puis on l'exécute ./fichier_perl.pl
.
Éléments de syntaxe
[modifier | modifier le wikicode]Commentaires
[modifier | modifier le wikicode]Sur une ligne
[modifier | modifier le wikicode]# ceci est un commentaire
Les commentaires se font sur une ligne et commencent à partir du caractère dièse # .
Sur plusieurs lignes : POD
[modifier | modifier le wikicode]Écrire un commentaire sur une ligne c'est pratique lorsque nous avons très peu de commentaire à faire, mais nous avons parfois besoin de beaucoup plus d'une ligne pour écrire une explication un peu plus longue. Dans ce cas, il est possible d'utiliser un mécanisme intégré à Perl qui a été conçu pour que les scripts comportent directement leur propre documentation : POD (Plain Old Documentation - Bonne Vieille Documentation). Avec ce système, nous allons écrire notre propre documentation directement au milieu de nos lignes de code (comme il se fait de nos jours dans un bon nombre d'autres langages).
À savoir que tout code est commenté de cette manière sur le CPAN, le plus gros rassemblement de modules Perl. Nous reverrons le CPAN et les modules qui permettent de réutiliser du code déjà écrit (même par d'autres développeurs) plus tard.
Un exemple simple d'instructions POD (se trouve au milieu d'un code) :
=pod
Toujours commencer par =pod puis un saut de ligne.
J'écris ici un commentaire
qui s'étend sur plusieurs
lignes.
Après un saut de ligne, le début d'un
autre paragraphe.
=cut
La structure est très simple, on commence par =pod, un saut de ligne, on écrit nos commentaires, un saut de ligne et on termine par =cut. Cela permet d'écrire de la documentation très facilement, puis de la convertir en html pour la voir dans un navigateur web par exemple.
La directive =pod déclare le début du commentaire, =cut la fin (et donc la reprise de l'interprétation du code Perl).
Sachez qu'il est possible de faire de la documentation bien plus structurée que simplement plusieurs paragraphes séparés par un saut de ligne, afin qu'un formateur de code POD puisse le transformer en fichier LaTeX ou HTML plus facilement compréhensible. Cependant pour ne pas alourdir ce livre, je vous renvoie sur quelques tutoriels dont les liens se trouvent dans les ressources.
instructions
[modifier | modifier le wikicode]Chaque instruction est séparée par un point virgule (;). Les blocs d'instructions commencent par une accolade ouvrante ({) et se terminent par une accolade fermante (}). Contrairement au langage C, les instructions ne sont pas toujours dans ces blocs, nous verrons ça plus tard.
my $variable = 42 ;
# plusieurs instructions sur une ligne
my $a = 2; print "hello" ; print "\n"; $a = 5;
# bloc d'instruction
{
say "coucou"; # instruction dans un bloc
}
déclaration de variables
[modifier | modifier le wikicode]portée $variable;
portée des variables
[modifier | modifier le wikicode]Le mot clé my désigne une portée locale, c'est à dire limitée à un bloc d'instructions. Le mot clé our désigne une portée globale, c'est à dire visible partout dans le fichier (dans les différentes fonctions, par exemple). Lorsqu'une variable est visible, elle est lisible et modifiable.
Types de base
Les types
[modifier | modifier le wikicode]En Perl il n'y a que 3 types de base. Ils sont reconnaissables à leur sigil, qui est un caractère avant le nom de la variable ($, @, %). Nous parlerons également de références qui est un moyen, comme son nom l'indique, de faire référence à une autre variable (proche de la notion de pointeurs en C).
Scalaire
[modifier | modifier le wikicode]Sigil : $ . Le type scalaire est un type qui peut garder en mémoire diverses informations. Exemple :
my $a = 42 ; # ceci est un entier
my $b = 2.5 ; # ceci est un décimal
my $c = "Une chaîne de caractères";
my $d = 'Une autre chaîne';
Référence sur scalaire
[modifier | modifier le wikicode]my $refa = \$a; # on référence la variable $a
Déférencement sur scalaire
[modifier | modifier le wikicode]my $a = $$refa; # $a vaut la valeur de la variable référencée par $refa
Référence
[modifier | modifier le wikicode]Une référence est un scalaire qui permet d'accéder à l'adresse mémoire où est stockée un autre scalaire, un tableau, un tableau associatif ou même une fonction. C'est très utilisé pour passer des tableaux en paramètre d'une fonction, ou tout autre élément que nous souhaiterions modifier sans passer par des copies de leurs valeurs. Nous allons voir par la suite comment les manipuler.
Tableau
[modifier | modifier le wikicode]Sigil : @ . Un tableau permet de stocker une liste. Une liste permet de stocker plusieurs éléments (des scalaires, des références). Une liste peut également être stockée dans un tableau associatif.
Voyons d'abord les tableaux :
my @tableau1 = ( "chaine1", "chaine2", "chaine3" ); # tableau de mots
my @tableau2 = ( @tableau1, "chaine4" ); # tableau de mots composé des mots de tableau1 + "chaine4"
accéder à une valeur d'un tableau
[modifier | modifier le wikicode]Il suffit d'utiliser les crochets [] et d'indiquer l'indice de l'élément recherché. À retenir : la numérotation commence à 0.
my $chaine = $tableau[2]; # accès au troisième élément du tableau @tableau
Référence sur tableau
[modifier | modifier le wikicode]my $ref = [ "chaine1", "chaine2", "chaine3" ]; # référence à une liste nouvellement créée (sans passer par un tableau)
my $ref2 = \@liste; # référence d'un tableau précédemment créé
Déférencement de tableau
[modifier | modifier le wikicode]my @tableau = @$ref; # déférencement d'un tableau
Tableau associatif
[modifier | modifier le wikicode]Sigil : % . Un tableau associatif (ou table de hashage) est un type qui permet d'associer un scalaire à un autre.
my %tableau = ( cle1 => "valeur1", cle2 => "valeur2" );
accéder à une valeur du tableau
[modifier | modifier le wikicode]my $chaine = $tableau{"clé_chaine"};
Référence sur tableau associatif
[modifier | modifier le wikicode]my $ref = \%tableau; # référencement
Déférencement de tableau associatif
[modifier | modifier le wikicode]my %tableau = %$ref; # déférencement d'un tableau associatif
Contexte
[modifier | modifier le wikicode]En Perl, la notion de contexte est importante et très utile. Suivant comment vous faites appel à la une variable, suivant l'opérateur que vous utilisez ou l'expression utilisée, vous faites appel à un contexte particulier. Petite présentation des contextes les plus utilisés.
Contexte numérique
[modifier | modifier le wikicode]L'expression évaluée est considérée comme un nombre. Les opérateurs utilisés traiteront les scalaires comme des nombres, ainsi la chaîne de caractères 01.50 sera interprétée comme le nombre 1.5. Dans ce contexte, les listes et les tableaux renvoient leur nombre d'éléments. Dans ce contexte, la valeur renvoyée par un élément de type undef est 0.
Contexte (scalaire) de chaîne
[modifier | modifier le wikicode]Un nombre est considéré comme une chaîne, et nous pouvons par exemple l'afficher, l'insérer dans une autre chaîne, etc. Dans ce contexte, la valeur undef renvoie la chaîne vide : "".
Contexte de liste
[modifier | modifier le wikicode]Comme son nom l'indique, il permet de manipuler des listes et également les tableaux. Dans ce contexte, une liste renvoie ses éléments. Si une liste est undef alors elle renvoie une liste d'un élément undef.
Exemples
[modifier | modifier le wikicode]Un certain nombre d'opérateurs forcent le contexte.
contexte (scalaire) numérique
[modifier | modifier le wikicode]my $a = "10.0" + 5; # L'opérateur + force le contexte numérique et l'expression renvoie la valeur 15
my $b = @tableau ; # $b est de type scalaire (sigil $) donc le tableau renvoie son nombre d'éléments
Une manière de forcer le contexte scalaire est d'utiliser la fonction scalar
.
my @tab2 = scalar(@tab); # @tab2 contient comme premier élément le nombre d'éléments de @tab
contexte de liste
[modifier | modifier le wikicode]my @tableau2 = @tableau; # @tableau2 est de type liste (sigil @) donc @tableau renvoie sa liste d'éléments
À noter qu'il est possible de forcer le contexte de liste avec un scalaire comme variable à gauche de l'affectation.
my $a = @tableau; # contexte numérique, renvoie le nombre d'éléments de @tableau
my ($b) = @tableau; # contexte de liste, $b contient donc le premier élément de @tableau.
Précision : my ($a, $b, $c) = (1, 2, 3) est une façon correcte de faire des déclarations multiples et simultanées de variables.
my ($a, $b, $c) = @tableau; # contexte de liste, $a, $b, $c contiennent donc respectivement les trois premiers éléments de @tableau.
Fonctions sur listes
Rappels sur les listes
[modifier | modifier le wikicode]Une liste est une suite de mots, de nombres… tout scalaire à vrai dire. Une liste stockée forme un tableau ou un tableau associatif (que nous verrons plus loin).
une liste
[modifier | modifier le wikicode]Pour créer une liste on utilise les parenthèses () de cette manière :
( "5.0", "truc" , 7, $autre_valeur )
Nous avons là une suite de scalaires : "5.0", "truc", 7 et la valeur stockée dans la variable scalaire $autre_valeur.
créer un tableau
[modifier | modifier le wikicode]Pour pouvoir la manipuler plus tard dans le programme, nous la stockons dans une variable. La variable est donc un tableau signalé par le sigil @.
Exemple avec parenthèses :
my @bon_a_manger = ( "poire", "chocolat", "sucre", "sirop", "bonbons", "fromage" );
Mais il y a d'autres opérateurs dédiés à la création de listes, comme qw qui crée une liste de mots (quoted words). Cet opérateur est préféré aux parenthèses dû à sa concision.
Exemple avec qw :
my @bon_a_manger = qw/poire chocolat sucre sirop bonbons fromage/;
La création de la liste est plus concise. Mais ceci va plus loin encore (voir la création de tableaux associatifs avec qw).
Créer un tableau associatif
[modifier | modifier le wikicode]Un tableau associatif (ou table de hachage) est une structure permettant d'associer à un scalaire un autre scalaire. C'est une liste un peu plus complexe qu'un tableau et le sigil est différent (%).
La création de ce type de liste se fait également avec des parenthèses, mais on ajoute également (facultatif) un opérateur => pour voir facilement la clé et sa valeur :
my %bon_a_manger = ( "fruit" => "cerise", "légume" => "patate" );
À savoir : l'opérateur => force l'interprétation de la variable à sa gauche comme une chaîne de caractères, donc les guillemets sont inutiles. Même exemple, mais avec qw :
my %bon_a_manger = qw/fruit cerise légume patate/;
Là encore nous voyons la concision apportée par cet opérateur. %bon_a_manger a le sigil % donc Perl sait qu'il doit ranger les mots par paires de la manière clé-valeur ("fruit" => "cerise", "légume" => "patate").
Fonctions de manipulation de listes
[modifier | modifier le wikicode]Il existe un nombre assez conséquent de fonctions de manipulation de listes (tableaux, tableaux associatifs). Nous allons aborder les plus courantes. Il n'est pas obligatoire de les connaître toutes par cœur.
.. : créer une suite d'éléments
[modifier | modifier le wikicode]On pourrait vouloir par exemple créer des suites longues d'éléments qui s'enchaînent. Par exemple, avec l'opérateur .. écrire une suite de 1 à 100 n'est pas plus compliqué que :
( 1 .. 100 ) # toutes les valeurs entières de 1 à 100
Nous pouvons faire également la même chose avec des lettres :
( 'a' .. 'z' ) # renvoie toutes les lettres de l'alphabet
exemple : les lettres de 'a' à 'zz'
[modifier | modifier le wikicode]Nous allons afficher à l'écran toutes les combinaisons des lettres de 'a' à 'zz' :
foreach my $var ( 'a' .. 'zz' )
{
say $var; # affiche tour à tour chacune des valeurs de 'a' à 'zz'
}
join : concatène tous les éléments d'une liste
[modifier | modifier le wikicode]La commande join permet de concaténer tous les éléments d'une liste avec un motif (une chaîne de caractères par exemple) entre chaque élément.
Exemple : afficher tous les éléments d'un tableau séparés par un double point.
my @fruits = qw/ poire cerise pomme fraise /;
say join(' : ', @fruits);
Affichera : « poire : cerise : pomme : fraise ».
map : transforme tous les éléments d'une liste
[modifier | modifier le wikicode]Conseil : pour comprendre correctement cette fonction, voyez au moins le cours sur les fonctions et les procédures.
Cette fonction applique une transformation à tous les éléments d'un tableau ou d'une table de hachage. map prend en argument un bloc de code à exécuter et une liste (ou tableau). Il retourne une liste d'éléments modifiés.
Un exemple simple, on va doubler les valeurs d'un tableau :
my @simple = ( 1 .. 3 );
my @double = map { $_ * 2 } @simple; # on double toutes les valeurs
say foreach @double; # 246
Comme plus haut mais en une seule ligne :
say foreach map { $_ * 2 } ( 1 .. 3 ) ; # ce code est un peu plus dur à lire que les autres, n'hésitez pas à le relire plusieurs fois
Écrire du code aussi compact est très pratique à l'usage, mais pour un débutant il n'est pas nécessaire de savoir écrire de façon aussi concise.
grep : récupère une partie des éléments d'une liste
[modifier | modifier le wikicode]Se manipule comme map, et permet de récupérer une liste d'éléments contenus dans une liste suivant certaines conditions. Exemple : les éléments numériques inférieurs à 10 :
my @tableau_1_a_100 = ( 1 .. 100 );
my @inferieurs_a_10 = grep { $_ < 10 } @tableau_1_a_100 ;
say foreach @inferieurs_a_10; # affiche chaque élément de @inferieurs_a_10 suivit d'un retour à la ligne
defined : savoir si un élément de la liste est défini
[modifier | modifier le wikicode]exists : savoir si un élément d'une liste existe
[modifier | modifier le wikicode]Cette fonction permet de savoir si un élément d'une liste existe. Attention : ceci ne permet pas de savoir si l'élément est défini.
delete : supprimer un élément d'une table de hachage
[modifier | modifier le wikicode]keys : retourne toutes les clés d'une table de hachage
[modifier | modifier le wikicode]Renvoie une liste des clés d'une table de hachage.
my %hash = qw/ fruit poire légume carotte/;
say join(' : ', keys (%hash));
Affiche : « légume : fruit ».
values : retourne toutes les valeurs d'une table de hachage
[modifier | modifier le wikicode]Renvoie une liste des valeurs contenues dans une table de hachage.
my %hash = qw/ fruit poire légume carotte/;
say join(' : ', values(%hash));
each : retourne un couple clé-valeur d'une table de hachage
[modifier | modifier le wikicode]Cette fonction est très utile pour boucler sur tous les éléments d'un tableau associatif.
my %hash = qw/ fruit poire légume carotte/;
say "Un couple 'clé - valeur' : ", join( " - " , each %hash);
Affichera : « Un couple 'clé - valeur' : légume - carotte » .
pop : retire le dernier élément d'une liste et le retourne
[modifier | modifier le wikicode]Permet de supprimer le dernier élément d'une liste. Il retourne cet élément, donc on peut le récupérer.
my @fruits = qw/ poire cerise pomme fraise /;
say pop @fruits; # affichera "fraise"
# @fruits ne contient plus que "poire", "cerise" et "pomme"
push : ajoute à la fin de la liste un nouvel élément
[modifier | modifier le wikicode]Cette fonction ajoute un élément à la fin d'une liste.
On reprend l'exemple plus haut en ajoutant push pour remettre « fraise » :
my @fruits = qw/ poire cerise pomme fraise /;
say pop @fruits; # affichera "fraise"
# @fruits ne contient plus que "poire", "cerise" et "pomme"
push @fruits, "fraise"; # @fruits contient à nouveau "fraise" en dernière position
shift : supprime le premier élément d'une liste et le retourne
[modifier | modifier le wikicode]Il suffit de faire shift @tableau, exemple :
my @bon_a_manger = qw/fruit cerise légume patate /; # un simple tableau
my $aliment = shift @bon_a_manger; # on supprime le premier élément du tableau et on le récupère (facultatif)
say $aliment; # affichera « fruit » qui était le premier élément de @bon_a_manger
say join ':', @bon_a_manger; # affichera « cerise:légume:patate »
unshift : ajoute un élément au début d'une liste
[modifier | modifier le wikicode]Exemple simple d'utilisation de unshift :
my @bon_a_manger = qw/fruit cerise légume patate /; # un simple tableau
my $aliment = "fromage"; # un simple scalaire
unshift @bon_a_manger, $aliment; # on ajoute le scalaire "fromage" au début du tableau @bon_a_manger
say join ':', @bon_a_manger; # affichera « fromage:fruit:cerise:légume:patate »
reverse : inverse tous les éléments d'une liste (le dernier devient premier, etc.)
[modifier | modifier le wikicode]On fait simplement reverse @tableau.
Le tri alphabétique inverse en devient on ne peut plus clair.
#!/usr/bin/perl -w
use strict;
my @tableau = qw(d a c e b);
my @tri = reverse sort @tableau; # e d c b a
print "@tri\n";#
splice : manipule des tranches de listes
[modifier | modifier le wikicode]#!/usr/bin/perl
use strict;
use warnings;
# splice est aux listes d‘items ce que
# substr est aux chaînes de caractères
# extraction
my @tableau= qw(ba be bi bo bu fa fe fi fo fu);
print splice (@tableau,2,3)."\n"; # bu dernier éliminé après bi et bo
print "@tableau\n";# ba be fa fe fi fo fu
# extraction du 5e à l'antépénultième
@tableau= qw(ba be bi bo bu fa fe fi fo fu);
splice(@tableau,4,-2);
print "@tableau\n";# ba be bi bo fo fu
# comptage depuis la fin
@tableau= qw(ba be bi bo bu fa fe fi fo fu);
splice(@tableau,-5,2);
print "@tableau\n";# ba be bi bo bu fi fo fu
# jusqu‘à l'avant-dernier
@tableau= qw(ba be bi bo bu fa fe fi fo fu);
splice(@tableau,-4,-1);
print "@tableau\n";# ba be bi bo bu fa fu
# remplacement de 4 items par XX
@tableau= qw(ba be bi bo bu fa fe fi fo fu);
splice(@tableau,2,4,"XX");
print "@tableau\n";# ba be XX fe fi fo fu
# gommage = remplacement vide
@tableau= qw(ba be bi bo bu fa fe fi fo fu);
splice(@tableau,3,5);
print "@tableau\n";# ba be bi fo fu
# incrustation = remplacement sur une longueur de zéro
@tableau= qw(ba be bi bo bu fa fe fi fo fu);
splice(@tableau,4,0,"YY");
print "@tableau\n";# ba be bi bo YY bu fa fe fi fo fu
# préfixage = fonction unshift
@tableau= qw(ba be bi bo bu fa fe fi fo fu);
splice(@tableau,0,0,"PREF_");
print "@tableau\n";# PREF_ ba be bi bo bu fa fe fi fo fu
# suffixage = fonction push
@tableau= qw(ba be bi bo bu fa fe fi fo fu);
my $Nombre = @tableau;
splice(@tableau,$Nombre,0,"_SUFF");# ba be bi bo bu fa fe fi fo fu _SUFF
print "@tableau\n";
scalar : retourne le nombre d'éléments d'une liste
[modifier | modifier le wikicode]Cette fonction n'a aucune utilité si on est en contexte scalaire, par exemple :
$nb_elements = @tableau;
Cependant, lorsque nous sommes en contexte de liste, il sera déjà plus utile :
my @tableau_1 = qw/voiture avion vélo/; # un tableau quelconque
my @tableau_2 = qw/train skateboard moto/; # un second tableau quelconque
# ici, nous souhaitons stocker le nombre d'éléments de chaque tableau
# et ne pas simplement créer un tableau contenant toutes les valeurs des deux tableaux
my @nombres_elements = (scalar(@tableau_1), scalar(@tableau_2));
Ici, le code que nous avons créé permet de créer un tableau @nombres_elements qui contient le nombre d'éléments de deux précédents tableaux. Sans scalar, nous aurions un tableau contenant l'ensemble des scalaires des deux premiers tableaux. Cela est dû à l'aplatissement des tableaux.
sort : ordonne une liste
[modifier | modifier le wikicode]#!/usr/bin/perl
use strict;
use warnings;
my @tableau = qw(9 -.2 -4 34 -7 0 111);
# tri implicitement alphabétique
my @tri = sort @tableau;
print "@tri\n";# -.2 -4 -7 0 111 34 9
# tri explicitement alphabétique
@tri = sort ({$a cmp $b} @tableau);
print "@tri\n";# -.2 -4 -7 0 111 34 9
# tri inverse alphabétique
@tri = sort ({$b cmp $a} @tableau); # autre syntaxe : @tri = reverse sort @tableau;
print "@tri\n";# 9 34 111 0 -7 -4 -.2
# tri explicitement numérique
@tri = sort ({$a <=> $b} @tableau);
print "@tri\n";# -7 -4 -.2 0 9 34 111
# tri inverse numérique
@tri = sort ({$b <=> $a} @tableau);
print "@tri\n";# 111 34 9 0 -.2 -4 -7
# tris tarabiscotés sans intérêt autre que montrer les possibilités de "sort"
@tableau = qw(3 1 6 5 3 1 6 5);
# tri croissant sur une moitié, décroissant sur l‘autre
my $moitie = scalar @tableau / 2; # scalar est facultatif : my $... = environnement scalaire
@tri = ((sort ({$a <=> $b} splice (@tableau,0,$moitie))),(sort ({$b <=> $a} splice (@tableau,-$moitie,$moitie))));
print "@tri\n";# 1 3 5 6 6 5 3 1
@tableau = qw(9 -.2 -4 -14 -7 0 11);
# tri aux normes croissantes
@tri = sort ({abs $a <=> abs $b} @tableau);
print "@tri\n";# 0 -.2 -4 -7 9 11 -14
@tableau = qw(ab a abcd abc abcde);
# tri aux longueurs décroissantes
@tri = sort ({length $b <=> length $a} @tableau);
print "@tri\n";# abcde abcd abc ab a
Opérateurs
Opérateurs numériques
[modifier | modifier le wikicode]Perl reprend tous les opérateurs classiques (+, -, *, /, %, sin, cos) et les étend.
Il y a des raccourcis utiles à connaître pour l'affectation, comme ++, --, +=, -=, *=, %=
Il existe également l'opérateur exponentiel **
.
exemples
[modifier | modifier le wikicode]my $a = 1 ;
$a--; # $a vaut 0 ; est équivalent à "$a = $a - 1;"
$a = $a + 2; # $a vaut 2 ; est équivalent à "$a += 2;"
$a *= 2; # $a vaut 4 ; est équivalent à "$a = $a * 2;"
$a -= 10; # $a vaut -6 ; est équivalent à "$a = $a - 10;"
$a++; # $a vaut -5 ; etc.
Il faut être attentif aux opérations avec ++ et -- .
$b = $a++
affecte à $b la valeur de $a **avant** son incrémentation.
$b = ++$a
affecte à $b la valeur de $a **après** son incrémentation.
Opérateurs de chaînes
[modifier | modifier le wikicode]Les opérateurs les plus basiques qui travaillent sur des chaînes sont :
- . : concaténation
- length : connaître la taille d'une chaîne
- chop : retire le dernier caractère d'une chaîne
- chomp : comme chop mais seulement si le dernier caractère est un retour à la ligne
- split : permet de couper une chaîne suivant un motif
- join : regroupe les éléments d'une liste, avec une chaîne entre tous les éléments
- reverse : inverse les éléments d'une liste (le premier devient le dernier et inversement)
- substr : prend une partie de la chaîne
- index : cherche l'indice de la première occurrence d'une chaîne dans une autre
- rindex : inverse de la fonction index, cherche la dernière occurrence
exemples
[modifier | modifier le wikicode]my $str = "coucoux";
say length( $str ), " ", index( $str, "uc" ); # affiche "7 2"
chop $str; # supprime la lettre 'x'
say $str; # affichera "coucou"
my @tableau_de_caracteres = split("", $str); # contient la liste des lettres restantes
say join(" | ", @tableau_de_caracteres); # affiche c | o | u | c | o | u
explications
[modifier | modifier le wikicode]say length( $str ), " ", index( $str, "uc" );
Ici, nous appellons la fonction say pour qu'elle affiche la taille de la chaîne de caractères $str (7), un espace (" ") et l'index de la première occurrence de "uc" (2). Nous verrons plus tard comment se passent les passages de paramètres et retours de fonctions.
my @tableau_de_caracteres = split("", $str);
La fonction split permet de séparer une chaîne de caractères suivant un motif, ici la chaîne vide : "". Ceci a pour conséquence de séparer toutes les lettres.
say join(" | ", @tableau_de_caracteres);
Nous affichons le retour de la fonction join qui renvoie tous les éléments de @tableau_de_caracteres séparés par la chaîne " | ". Elle affiche donc "c | o | u | c | o | u".
Tests
Les tests
[modifier | modifier le wikicode]Il existe plusieurs manières d'effectuer un test, qui est couramment appelé « évaluer une expression » en jargon informatique. Nous allons voir les méthodes les plus courantes.
Les mots clés
[modifier | modifier le wikicode]- if, elsif, else, unless
Le mot clé utilisé pour faire des tests est if (ou unless si on veut tester négativement). Si le test échoue, nous pouvons mettre la condition "sinon si" : elsif. Nous pouvons enchaîner autant de elsif que nous le souhaitons. Un bloc de code est exécuté si tous les autres tests précédents ont échoué via le mot clé else. Unless peut être vu comme l'inverse de if (si n'est pas).
Syntaxe
[modifier | modifier le wikicode]Syntaxe classique if elsif else
[modifier | modifier le wikicode]Nous allons voir comment effectuer des tests et exécuter différentes parties du code en fonction des valeurs de nos variables. Cette syntaxe est particulièrement connue car utilisée dans de nombreux langages.
if ( expression )
{
instructions
}
elsif ( expression )
{
instructions
}
else
{
instructions
}
Attention : il ne faut pas mettre de point virgule (;) à la fin de toutes les lignes, relisez bien le code pour savoir où les placer ! Contrairement à certains langages, nous ne pouvons pas ommettre les accolades ({}) dans cette version du test.
exemple
[modifier | modifier le wikicode]my $a = 5;
my $b = 8;
if ( $a > $b )
{
say "$a est plus grand que $b";
}
elsif ( $a < $b )
{
say "$a est plus petit que $b";
}
else
{
say "$a et $b sont égaux";
}
condition après instruction conditionnée
[modifier | modifier le wikicode]instruction if ( expression ) ;
Ce code est bien plus court, et avec cette syntaxe nous n'avons pas besoin de mettre les parenthèses.
exemple
[modifier | modifier le wikicode]my $a = 6;
my $b = 5;
say $a if $a > $b;
Ce code affiche "6" car $a > $b. Cependant, cette façon d'écrire le test d'une série d'expressions ne permet pas l'utilisation de elsif, de else, et ne permet l'exécution conditionnelle que d'une seule instruction.
Cette méthode permet de lire l'instruction conditionnée comme si on lisait une phrase => "fais ceci SI cela".
Syntaxe condition ternaire
[modifier | modifier le wikicode]expression ? instruction_si_vrai : instruction_si_faux ;
exemple
[modifier | modifier le wikicode]my $a = 6;
my $b = 5;
$a > $b ? say "$a plus grand que $b" : say "$a plus petit que $b" ;
$a ( valeur = 6) est plus grand que $b (valeur = 5) donc on affiche "$a plus grand que $b".
Opérateurs de tests
[modifier | modifier le wikicode]Il y a un certain nombre d'opérateurs qui permettent de faire des tests sur des nombres (scalaires numériques), des chaînes (toujours des scalaires) ou des listes.
Opérateurs de tests numériques
[modifier | modifier le wikicode]Tout d'abord, les tests de comparaisons classiques comme dans les autres langages (et en mathématiques) :
- < , > , <= , >= , == , !=
Ils nous permettent de faire des tests très simples "si $a plus grand que $b". Un peu moins répandu :
- <=>
#!/usr/bin/perl -w
use strict;
# opérateur <=>, ses 3 réponses possibles
my ($a,$b)= (5,6);
print ($a<=>$b);# -1
print "\n";
print ($b<=>$a);# 1
print "\n";
($a,$b)= (5,5);
print ($a<=>$b);# 0
print "\n";
Opérateurs de tests sur chaînes
[modifier | modifier le wikicode]Ces opérateurs travaillent sur des chaînes de caractères. Une chaîne "plus petite" qu'une autre est une chaîne qui apparaît avant dans l'ordre lexicographique.
- lt (plus petit que) , gt (plus grand que),
- le (plus petit ou égal) , ge (plus grand ou égal) ,
- eq (égal) , ne (non égal).
exemple
[modifier | modifier le wikicode]my $chaine1 = "coucou";
my $chaine2 = "zzz";
if( $chaine1 lt $chaine2 ) # rappel : lt = plus petit que dans ordre lexicographique
{
say $chaine1;
}
elsif( $chaine2 lt $chaine1 ) # si $chaine2 arrive plus tôt dans l'ordre lexicographique que $chaine1
{
say $chaine2;
}
else
{
say "Les chaînes sont égales !";
}
valeurs booléennes retournées: 1 ou vide
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
my ($a,$b)= (5,6);
print ($a lt $b);# 1
print "\n";
print">>>";
print ($a gt $b);# >>><<< chaine vide mais définie !
print "<<<\n";
print defined ($a gt $b);# 1
Opérateurs pour tester plusieurs expressions
[modifier | modifier le wikicode]Commençons par les mots clés :
- or, and, : « ou » et « et »
- ||, && : « ou » et « et »
Nous verrons plus tard la notion de précédence.
Ce qu'il faut savoir pour évaluer plusieurs expressions est que l'on peut combiner des expressions pour en former une seule grande. Comme nous l'avons vu, une expression est souvent un test entre parenthèses :
( $a >= $b )
Nous pouvons créer une expression plus grande, combinant celle-ci avec d'autres :
(($a >= $b) || ($a == 5))
Ici, nous évaluons la première expression ($a >= $b) et avec le « ou » (les deux barres verticales ||) nous évaluons la seconde expression seulement si la première a échouée ($a est un nombre inférieur à $b). Si à la place du « ou » (||) nous avions un « et » (&&), il faudrait que les deux expressions qui l'entourent soient testées positivement pour que l'expression globale soit vraie.
exemple
[modifier | modifier le wikicode]my $a = 5 ;
my $b = 10 ;
say "$a" if( ($a < $b && ($a * 2) > $b) || $a == 5);
On affiche $a si ($a est inférieur à $b et que son double ($a * 2) est supérieur à $b) ou si $a est égal à 5. $a ne correspond pas au premier test entre parenthèses ($a < $b && ($a * 2) > $b) car une des deux conditions nécessaires n'est pas remplie. Donc le premier test est négatif et on va exécuter la seconde série de tests après le « ou » (||), $a est égal à 5, donc la série globale est validée. On affiche 5 (la valeur de la variable $a).
Itérations
Boucles et syntaxe
[modifier | modifier le wikicode]Les boucles permettent simplement de ré-exécuter un bloc de code. Perl offre des outils très puissants pour parcourir aisément des listes (tableaux, tableaux associatifs).
Boucle while et until
[modifier | modifier le wikicode]Boucle classique, déjà vue dans un bon nombre de langages.
Syntaxe
[modifier | modifier le wikicode]while( expression ) { instructions }
until( expression ) { instructions }
La seule différence entre while et until est que while boucle tant que l'expression est vraie, until jusqu'à ce qu'elle le soit.
exemples
[modifier | modifier le wikicode]# while
my @fruits = qw/ cerise pomme pastèque /;
while( @fruits ) # une fois la liste @fruits vide, l'expression sera évaluée fausse
{
say pop @fruits; # rappel : pop supprime le dernier élément et le retourne
}
# until
@fruits = qw/ cerise pomme pastèque /;
until ( !(@fruits) ) # liste vide = faux, !faux = vrai, until s'arrête
{
say pop @fruits; # pastèquepommecerise
}
Tant qu'il reste des éléments dans @fruits on boucle, et à chaque itération on pop (suppression du dernier élément d'une liste) un élément qu'on affiche.
Boucle for
[modifier | modifier le wikicode]Permet de boucler comme dans le langage C.
Syntaxe
[modifier | modifier le wikicode]for([expression] ; [expression] ; [expression]) { instructions }
La première expression est ce qui est effectué avant de commencer à boucler, la seconde est un test qui est effectué à chaque début de boucle, pour savoir si on continue de boucler, la dernière est une expression qui s'effectue à chaque fin d'itération.
Vous aurez remarqué que les 3 expressions sont écrites entre crochets [], c'est parce qu'elles sont toutes les 3 facultatives.
exemples
[modifier | modifier le wikicode]my @fruits = qw/ cerise pomme pastèque /;
for(my $i = 0 ; $i < @fruits ; $i++) # $... = contexte scalaire donc "scalar @fruits" est facultatif
{
say $fruits[$i]; # $i ème élément de la liste @fruits
}
explications
[modifier | modifier le wikicode]for(my $i = 0 ; $i < @fruits ; $i++)
Première expression : on crée la variable $i qu'on initialise à 0. Seconde expression : on teste si $i est inférieure à la taille de @fruits. Dans cette condition, on utilise l'opérateur numérique < qui force le contexte scalaire, donc @fruits renvoie son nombre d'éléments. Troisième expression, on incrémente $i (à chaque fin de boucle).
say $fruits[$i];
On affiche la ième valeur de la liste @fruits. Ceci se rapproche de la manière de boucler sur des listes et tableaux dans d'autres langages.
Boucle do while
[modifier | modifier le wikicode]La différence entre une boucle while comme vu plus haut et une boucle do while est qu'on rentre déjà une première fois dans la boucle même si la condition du while n'est pas vraie.
Syntaxe
[modifier | modifier le wikicode]do { instructions } while (expression);
do { instructions } until (expression);
exemples
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
my $valeur_max = 6;
my $indice = 0;
do
{
say "$indice "; # 0 1 2 3 4 5
} while ++$indice < $valeur_max ;
print "\n";
# $indice > 6
$indice = 9;
do
{
say "$indice "; # 9 tout seul
} while ++$indice < $valeur_max ;
On effectue la boucle de toutes façons une fois. Si $indice est déjà égal ou supérieur à $valeur_max on l'affichera au moins une fois.
Boucle foreach
[modifier | modifier le wikicode]Permet de parcourir toute une liste (tableau, tableau associatif).
Syntaxe
[modifier | modifier le wikicode]foreach my $variable ( tableau ) { instructions }
instruction foreach ( tableau ) ;
exemples
[modifier | modifier le wikicode]my @fruits = qw/ cerise pomme pastèque /;
foreach my $fruit (@fruits)
{
say "$fruit "; # cerise pomme pastèque
}
Un exemple simple pour montrer la concision d'un code Perl. Nous verrons plus loin les mécanismes qui permettent ceci :
my @fruits = qw/ cerise pomme pastèque /;
say foreach "@fruits";
Boucle sur un tableau associatif
[modifier | modifier le wikicode]Il y a plusieurs façons de faire.
Syntaxe
[modifier | modifier le wikicode]foreach my $cle (keys %tab_asso) { instructions } # clé courante = $cle et valeur courante = $tab_asso{$cle}
while (my ($cle, $valeur) = each %tab_asso) { }
exemples
[modifier | modifier le wikicode]En utilisant foreach et keys :
Contrairement à "for", l‘ordre de sortie est aléatoire, néanmoins tout y est !
my %tab_asso = qw/ a 1 b 2 c 3 d 4 e 5/;
foreach my $cle (keys %tab_asso)
{
say $cle , $tab_asso{$cle}," "; # c3 d4 e5 a1 b2 ou bien d4 b2 e5 a1 c3 ou bien …
}
Avec while et each :
my %tab_asso = qw/ a 1 b 2 c 3 d 4 e 5/;
while (my ($cle, $valeur) = each %tab_asso)
{
say $cle , $valeur," "; # e5 a1 d4 c3 b2 ou bien …
}
Fonctions et procédures
Introduction
[modifier | modifier le wikicode]Quelques définitions
[modifier | modifier le wikicode]Déjà, il est bon de savoir ce qu'est une fonction. Une fonction permet de factoriser du code, si on a besoin d'exécuter une même portion de code à des endroits différents, ou pour simplifier la lecture d'un code, il est préférable de séparer certaines instructions et d'y faire appel le moment venu.
Petit instant vulgarisation : un exemple simple pour se représenter ce qu'est une fonction est de s'imaginer toutes les actions que nous avons besoin de faire pour aller faire les courses au marché. Vous dites « aller faire les courses » et pas « prendre mes clés, mon argent, sortir de chez moi, fermer la porte, prendre la voiture … ». Une fonction permet donc ceci: « s'abstraire » de toutes les actions entre le moment où vous partez et le moment où vous revenez. Ce qu'il faut pour que cette série d'action se déroule bien, ce serait avoir un peu d'argent ; prenez ceci comme exemple de paramètre. Pour aller faire les courses (la fonction), il faut un peu d'argent (paramètre de la fonction), et à la fin vous avez vos courses (retour de fonction).
Ce qu'est une fonction en Perl
[modifier | modifier le wikicode]En Perl, il y a plusieurs manières d'appeler ou même de faire des fonctions, tout comme il y a plusieurs manières de tout faire. Il n'y a pas forcément une méthode meilleure que l'autre, si ce n'est au niveau de la visibilité du point de vue d'un œil non averti.
Nous commencerons à voir comment se fait l'appel d'une fonction (que nous avons vu sans en parler jusque-là), puis comment créer sa propre fonction et comment récupérer les paramètres.
À savoir : généralement en Perl nous parlons de « routine » pour dire une fonction que vous créez vous-même. Le mot « fonction » est généralement utilisé pour parler des fonctions intégrées directement dans le langage.
Les appels de fonction
[modifier | modifier le wikicode]Nous avons déjà vu comment appeler une fonction, par exemple :
say "hello !";
Ici nous avons un appel à la fonction "say" et nous passons en paramètre "hello !". Plus classiquement dans les autres langages, des parenthèses sont utilisées pour distinguer la fonction de ses paramètres, ceci donnerait :
say( "hello !");
En Perl, il n'y a pas d'obligation. Pour passer plusieurs paramètres, Pour enchaîner plusieurs fonctions à la suite, il est parfois nécessaire de mettre des parenthèses pour lever l'ambigüité sur quels sont les paramètres utilisés pour quelle fonction.
say "début de ligne " , join ':', qw/mots du milieu/ , " fin de ligne."
# début de ligne mots:du:milieu: fin de ligne.
Si on cherche à écrire cette fonction de manière plus claire, ceci donne :
say("début de ligne ", join(':', qw/mots du milieu/, " fin de ligne."));
On remarque plus facilement la différence entre les paramètres de say et ceux de join. Aussi, si on avait envie que join ne prenne que ':' et qw/mots du milieu/ en paramètre, il aurait fallu écrire :
say "début de ligne ", join(':', qw/mots du milieu/), " fin de ligne.";
Assez souvent une fonction renvoie une valeur. Dans l'exemple précédent, la fonction join renvoie une chaîne de caractères (un scalaire) et on réutilise tout de suite cette valeur comme paramètre à l'appel de la fonction say. On aurait évidemment pû récupérer cette valeur :
my $chaine = join(':', qw/mots du milieu/);
say "début de ligne ", $chaine , " fin de ligne.";
Créer une fonction
[modifier | modifier le wikicode]Syntaxe
[modifier | modifier le wikicode]Cela se fait avec le mot clé sub (pour « subroutine » ) suivit du nom que vous souhaitez donner à la fonction, puis d'un bloc de code. La syntaxe la plus simple est :
sub nom_de_la_fonction
{
# instructions
}
Il n'y a pas de règle pour le placement des accolades, vous pouvez très bien mettre l'accolade ouvrante sur la même ligne que le nom de la fonction que vous créez si vous trouvez ça plus naturel.
Récupérer des paramètres envoyés à la fonction
[modifier | modifier le wikicode]Maintenant on va vouloir passer des paramètres à notre fonction. Il faut savoir qu'en Perl, il n'y a pas besoin d'indiquer les paramètres d'une fonction.
Pour récupérer les paramètres d'une fonction, vous pouvez prendre les valeurs qui se trouvent dans la variable spéciale @_. Cette variable fait partie des variables qui changent au cours du temps, en fonction du moment où elles sont appelés. Dans ce contexte (rien à voir avec les contextes des variables), @_ vaut simplement la liste de paramètres que vous envoyez à votre fonction. Comme cette variable change au cours du temps, il est préférable d'en faire une copie dans un tableau en tout début de fonction, pour récupérer les paramètres et être sûr de pouvoir les réutiliser par la suite.
sub afficher {
my @s = @_; # @_ contient actuellement tous les paramètres envoyés à la fonction
say join ' : ' , @s;
}
afficher("test");
Donc tout ce que vous avez à faire pour récupérer des paramètres c'est de récupérer @_. À savoir qu'il y a quelques petites astuces à connaître pour récupérer des paramètres facilement. Prenons un exemple, une fonction qui fait une addition (et prend 2 paramètres) et affiche le résultat :
sub addition
{
my ($a, $b) = @_;
my $resultat = $a + $b;
say $resultat;
}
Ici on peut voir qu'il y a des parenthèses autour des deux variables $a et $b ; ceci est pour forcer le contexte de liste. S'il y a plus de deux éléments dans @_ alors ils seront ignorés.
Retour de valeur
[modifier | modifier le wikicode]Pour que la fonction retourne une valeur, il suffit d'utiliser le mot clé return. On peut renvoyer un scalaire, une liste (tableau, table de hachage…).
sub renvoyer_une_chaine
{
return "Cette fonction renvoie une chaîne de caractères";
}
say renvoyer_une_chaine ; # affichage de la valeur de retour de la fonction
Il est possible également de renvoyer plusieurs valeurs, elles seront renvoyés dans une liste et la récupération de ces valeurs peut se faire comme pour la récupération des paramètres dans une fonction.
sub addition
{
my ($a, $b) = @_;
my $resultat = $a + $b;
return $a, $b, $resultat;
}
my ($a, $b, $resultat) = addition(5,6);
say "$a + $b = $resultat"; # 5 + 6 = 11
Pour renvoyer plusieurs résultats, il suffit de séparer les variables par des virgules, ce qu'on fait généralement pour séparer des paramètres à l'appel d'une fonction par exemple.
Les modules
Ce que sont les modules
[modifier | modifier le wikicode]Un module est une sorte de bibliothèque logicielle, qui possède des fonctionnalités réutilisables. Cela se présente sous la forme d'un fichier, où des tas de fonctionnalités sont écrites et qui touchent toutes à un type d'application, un protocole réseau, un usage, une thématique. Ce qui veut dire qu'un jour une personne a programmé un ensemble de fonctions qui vous permettent de faire quelque chose en particulier, comme par exemple créer une interface graphique ou jouer de la musique, et vous pouvez réutiliser le travail déjà réalisé, ce qui vous permet de gagner du temps.
Nous nous occuperons plus tard de la manière de créer un module, ici on va voir comment les utiliser.
Avant de commencer, sachez que les modules sont recherchés dans des répertoires. Cette liste est dans la variable globale @INC (INC pour include), pour l'afficher :
perl -E "say for @INC"
L'ordre des répertoires est important, car si deux modules du même nom se retrouvent dans deux répertoires différents, seul le premier trouvé sera utilisé. Vous avez également la commande :
perl -V
Cette commande vous permettra d'obtenir beaucoup plus d'informations, n'hésitez pas à essayer cette commande.
Une fois que vous avez installé Perl, vous possédez une « distribution de Perl », ce qui veut dire que vous avez déjà un certain nombre de modules les plus utilisés.
Connaître les modules déjà installés sur le système
[modifier | modifier le wikicode]Il y a deux options de perldoc à connaître pour savoir quels sont les modules installés. La première est pour savoir quels sont les modules installés par défaut avec votre « distribution » de Perl :
perldoc perlmodlib
La seconde est pour connaître les modules installés en plus (ceux que vous avez installé ainsi que les modules qui ont été installés en tant que dépendance à ces modules).
perldoc perllocal
Installer un module
[modifier | modifier le wikicode]Pour installer des modules, je vous conseille d'installer le logiciel cpanminus qui permet d'installer très simplement n'importe quel module en tapant simplement :
cpanm Le::Module
cpanminus gère également le téléchargement et l'installation des dépendances du module que vous cherchez à obtenir, il n'y a rien à faire.
Comment installer cpanminus
[modifier | modifier le wikicode]- Sous linux (TODO : à vérifier) (pensez à aller voir le site de votre distribution)
aptitude install cpanminus # Debian apt-get install cpanminus # Ubuntu yum install cpanminus # Fedora
Utiliser un module
[modifier | modifier le wikicode]Pour utiliser simplement un module :
use nomdumodule; # cas où le module est juste composé d'un fichier dans un des répertoires de @INC
use Le::Module; # dans le cas où le module se trouve dans le répertoire « Le » lui-même dans un des répertoires de @INC
Prenons la ligne 2, ce que ce code fait est qu'il va chercher dans une liste de répertoires le dossier « Le » dans lequel il y aurait le fichier « Module.pm » (.pm pour Perl Module).
Une fois le module chargé, il ajoute des mots dans notre espace de nommage, c'est à dire qu'il nous permet d'appelé de nouvelles fonctions.
Une fois que vous avez chargé un module, vous ne pouvez plus le décharger.
un exemple concret
[modifier | modifier le wikicode]On va chercher le répertoire courant via le module Cwd et la fonction getcwd.
use Cwd;
my $repertoire = getcwd(); # getcwd est une fonction dans le module Cwd
say "Nous sommes dans le répertoire $repertoire";
Si on n'avait pas inclut le module Cwd on n'aurait pas la fonction getcwd (pas dans notre espace de noms, fonction inconnue).
Voir la documentation d'un module
[modifier | modifier le wikicode]Vous pouvez voir la documentation de n'importe quel module en tapant :
perldoc Le::Module
bash$perldoc Cwd
Réponse :
System::xxx::xxx::Perl::5.18::xxx::Cwd(3)
NAME
Cwd - get pathname of current working directory
SYNOPSIS
use Cwd;
my $dir = getcwd;
use Cwd 'abs_path';
my $abs_path = abs_path($file);
DESCRIPTION
This module provides functions for determining the pathname of the
etc …
Où trouver des modules
[modifier | modifier le wikicode]On ne va voir ici que des sites où tous les modules sont gratuits et que vous pouvez utiliser librement. Pour trouver des modules et donc des bouts de programme déjà fait, vous avez deux sites web qui sont les références en Perl à savoir :
- search.cpan.org, CPAN (Comprehensive Perl Archive Network) : beaucoup de modules ;
- Méta-CPAN : apporte des outils très intéressants pour choisir le bon module dans l'annuaire du CPAN ;
À savoir : chacun des modules est documenté avec POD (rappel : Plain Old Documentation) et ces sites vous permettent de voir leur documentation sous la forme HTML (directement sur le site donc) ainsi que des informations sur ces modules, comme par exemple une note qui leur est attribuée par les autres personnes utilisant le CPAN.
Le méta-CPAN permet quant à lui d'obtenir des informations comme la compatibilité du module
Comment choisir un module
[modifier | modifier le wikicode]Pour bien choisir un module, nous prenons généralement un module avec un nom le plus générique possible, se rapprochant de ce que nous cherchons. Par exemple, si vous cherchez un module pour faire du réseau (échange de données entre différents ordinateurs tout simplement), vous allez chercher dans meta-cpan « network ».
Créer un module
[modifier | modifier le wikicode]todo
Chaînes de caractères
Le langage Perl permet de gérer les chaînes de caractères.
Comme il s'agit d'une langage ancien, il a d'abord été conçu pour gérer les anciens codages de textes (ASCII, iso-8859-x, etc.).
Le langage Perl s'adapte tout de même à son époque, et il est en particulier possible de traiter des textes Unicode. Si vous ne connaissez pas Unicode, vous pouvez le découvrir avec le livre À la découverte d'Unicode.
Des pages de manuels de Perl sont dédiées à l'Unicode, comme perlunicode ou perluniintro.
Fonctions de manipulation des chaînes
[modifier | modifier le wikicode]- substr
- Extrait une sous-chaîne.
#!/usr/bin/perl
use strict;
use warnings;
my $chaine="babebibobu";
# extraction
print substr($chaine,4,2)."\n"; # bi
# extraction du 5e à l'antépénultième
$chaine="babebibobu";
print substr($chaine,4,-2)."\n"; # bibo
# comptage depuis la fin
$chaine="babebibobu";
print substr($chaine,-7,2)."\n"; # eb
#
# jusqu‘à l'avant-dernier
$chaine="babebibobu";
print substr($chaine,-7,-1)."\n"; # ebibob
# remplacement de 4 caractères par XX
$chaine="babebibobu";
substr($chaine,2,4,"XX");
print $chaine."\n"; # baXXbobu
# gommage = remplacement vide
$chaine="babebibobu";
substr($chaine,2,2,"");
print $chaine."\n"; # babibobu
# incrustation = remplacement sur une longueur de zéro
$chaine="babebibobu";
substr($chaine,4,0,"YY");
print $chaine."\n"; # babeYYbibobu
# préfixage
$chaine="babebibobu";
substr($chaine,0,0,"PREF_");
print $chaine."\n"; # PREF_babebibobu
# suffixage
$chaine="babebibobu";
substr($chaine,length $chaine,0,"_SUFF");
print $chaine."\n"; # babebibobu_SUFF
###########################
# autre syntaxe
# remplacement
$chaine="babebibobu";
substr($chaine,2,3)="XX";
print $chaine."\n"; # baXXibobu
# incrustation
$chaine="babebibobu";
substr($chaine,6,0)="YY";
print $chaine."\n"; # babebiYYbobu
# gommage
$chaine="babebibobu";
substr($chaine,2,6)="";
print $chaine."\n"; # babu
# préfixage
$chaine="babebibobu";
substr($chaine,0,0)="PREF_";
print $chaine."\n"; # PREF_babebibobu
# suffixage
$chaine="babebibobu";
substr($chaine,length $chaine,0)="_SUFF";
print $chaine."\n"; # babebibobu_SUFF
- length
- Calcule la longueur d'une chaîne.
#!/usr/bin/perl -w
# -w = "use warnings;"
use strict;
my $chaine="babebibobu";
print length $chaine; # 10
print "\n";
# lettres accentuées du français = 2 octets
my $chaine2="àâéèêëîïôùûçœæ";
print length $chaine2; # 28
print "\n";
# caractères spéciaux
print length "\t"; # 1 = UNE tabulation
print "\n";
print length "\\\n"; # 2 = une \ littéralement + un retour de chariot
print "\n";
# accepte toute chaîne quel que soit l‘emberlificotage de fonctions qui la crèe.
print length "devant".substr($chaine,-7,-1)x 3 ."derrière\n".sprintf ("%.5f",6/7) ; # 41 dont devant = 6 ; eBIboB x 3 = 18 ; derrière\n = 10 (è = 2) ; 0.85714 = 7 caractères
print "\n";
- lc
- Transforme une chaîne en minuscules.
#!/usr/bin/perl -w
# -w = "use warnings;"
use strict;
my $chaine="BaBeBiBOBU";
print lc "$chaine.\n"; # babebibobu
my $chaine2="fichier.TXT";
print ucfirst substr( $chaine2,0,-4). lc substr( $chaine2,-4,4)."\n" ;# Fichier.txt
- uc
- Transforme une chaîne en majuscules.
#!/usr/bin/perl -w
# -w = "use warnings;"
use strict;
my $chaine="BaBeBiBOBU";
print uc $chaine."\n"; # BABEBIBOBU
- lcfirst
- Transforme le premier caractère d'une chaîne en minuscule.
#!/usr/bin/perl -w
# -w = "use warnings;"
use strict;
my $chaine2="Cornélien";
print lcfirst $chaine2."\n"; # cornélien
- ucfirst
- Transforme le premier caractère d'une chaîne en majuscule.
#!/usr/bin/perl -w
# -w = "use warnings;"
use strict;
my $chaine="corneille";
print ucfirst $chaine."\n"; # Corneille
my $chaine2="corneille, (auteur du cid.)";
print ucfirst substr( $chaine2,0,-5). ucfirst substr( $chaine2,-5,5)."\n" ; # Corneille, (auteur du Cid.)
- chop
- Supprime le dernier caractère d'une chaîne.
Il est utilisé surtout pour effacer les \n ("entrées" ou Carriage Return).
#!/usr/bin/perl -w
use strict;
# chop efface le dernier caractère de chaque item d'un liste
my @tableau = ( "a\n" , "b\n", "c\n" );
print @tableau; # a b c sur 3 lignes distinctes
chop @tableau;
print @tableau; # abc accolés : les \n ont disparu
print "\n";
# chop renvoie son dernier effacement
my @tableau2 = ( "vaches" , "veaux" );
print chop @tableau2;# le x de "veaux"
print "\n";
print @tableau2;# vacheveau
print "\n";
- chomp
- Permet de supprimer plusieurs caractères à la fin d'un chaîne.
#!/usr/bin/perl -w
use strict;
# création d‘un fichier cobaye de 3 lignes
# Descripteur de fichier = File Handle
open ( Descr0,'>','./cobaye') or die ("écriture impossible $!");
print Descr0 "premier\ndeuxieme\ntroisieme\n";
close(Descr0);
# lecture sans chomp -> affichage des 3 lignes
open (Descr1,'<','./cobaye') or die ("ouverture impossible $!");
while(defined (my $ligne = <Descr1> ) ){
print $ligne;
}
# chomp efface le \n de chaque ligne lue dans cobaye -> affichage sur une seule ligne
open (Descr2,'<','./cobaye') or die ("ouverture impossible $!");
while(defined ( my $ligne = <Descr2> ) ){
chomp $ligne;
print $ligne;
}
close(Descr2);
close(Descr1);
print "\n";
- reverse
- Inverse l'ordre des caractères d'une chaîne.
#!/usr/bin/perl -w
use strict;
# renversement d‘une liste
my @tableau = qw(premier deuxieme troisieme quatrieme);
print reverse @tableau; # quatriemetroisiemedeuxiemepremier
print "\n";
# renversement d‘une chaîne (le 3e item)
my $envers= reverse $tableau[2];
print $envers;# emeisiort
Expressions régulières
Introduction
[modifier | modifier le wikicode]Les expressions régulières sont des outils de l'informatique théorique permettant de décrire les langages de manière formelle. Elles sont implémentées dans beaucoup de langages de programmation dont Perl qui les utilisent avec une syntaxe étendue (fonction, code dans l'expression régulière, ...).
Ce chapitre peut vous être utile peu importe quel langage vous avez l'habitude de manipuler ou que vous utiliserez à l'avenir. N'hésitez pas à lire ce chapitre en plusieurs fois pour apprécier toutes les subtilités et pour comprendre à quel point ceci fait partie intégrante du langage Perl et comment bien se servir de ces outils.
Définition
[modifier | modifier le wikicode]Une expression régulière est une manière de manipuler du texte de façon concise et claire à l'aide de motifs. Les expressions régulières (ou « rationnelles », aussi appelées « regex » pour « regular expression ») sont issues des théories mathématiques sur les langages formels.
Format
[modifier | modifier le wikicode]Tout d'abord, nous utilisons le symbole =~ pour utiliser des expressions régulières, ensuite nous avons toute une panoplie de fonctionnalités à appliquer sur un texte (recherche, remplacement…), avec des options disponibles. Pour cela, voici la syntaxe :
$texte =~ fonction_voulue/première zone de texte[/seconde zone de texte si la fonction voulue le requiert]/[options];
Pour rappel, ce qui est entre crochets [] n'est pas toujours obligatoire. Le remplacement de texte par exemple nécessitera de rechercher du texte (première zone de texte) puis de le remplacer par un autre texte (seconde zone de texte). Pour la recherche de texte, pas besoin d'indiquer la seconde zone.
À savoir : cet exemple utilise des slashs (/) cependant nous pouvons utiliser d'autres caractères, comme l'underscore, un point d'exclamation, ou encore des accolades par exemple. Ceci est pratique lorsque nous avons à chercher un motif contenant des slashs (pas besoin de le banaliser, c'est à dire rajouter un antislash « \ » devant le caractère).
Exemple
[modifier | modifier le wikicode]Un exemple simple d'utilisation serait de chercher un mot dans un texte, fonctionnalité m (Match en anglais) :
my $texte = "J'aime le fromage";
# la fonctionnalité "m" correspond à la recherche de motif
if( $texte =~ m/fromage/ ) # on n'utilise pas d'options
{
say "On a trouvé « fromage » dans le texte";
}
else
{
say "On n'a pas trouvé de fromage :(";
}
Comme on peut s'y attendre, ce code va afficher « On a trouvé « fromage » dans le texte » puisque nous recherchons le mot « fromage » dans $texte. En fait, la fonction cherche à savoir si le texte correspond (match en anglais) au motif spécifié. Celui-ci ne contenant pas d'ancre de début ou fin de texte, peut correspondre avec une partie se trouvant au milieu du texte.
Fonctionnalités
[modifier | modifier le wikicode]Les expressions rationnelles peuvent être analysées et testées via un débogueur en ligne comme https://regex101.com/.
Caractère | Type | Explication |
---|---|---|
.
|
Point | N'importe quel caractère |
[...]
|
crochets | classe de caractères : tous les caractères énumérés dans la classe, avec possibilité de plages dont les bornes sont séparées par "-". Ex : [0-9a-z] pour tout l'alphanumérique en minuscule, ou [0-Z] pour tous les caractères de la table Unicode entre "0" et "Z", c'est-à-dire l'alphanumérique majuscule plus ":;<=>?@"[1].
|
[^...]
|
crochets et circonflexe | classe complémentée : tous les caractères sauf ceux énumérés. |
^
|
circonflexe | Marque le début de la chaîne ou de la ligne. |
$
|
dollar | Marque la fin de la chaîne ou de la ligne. |
|
|
barre verticale | Alternative - ou reconnaît l'un ou l'autre |
(...)
|
parenthèses | groupe de capture : utilisé pour limiter la portée d'un masque ou de l'alternative, grouper un motif répété ou capturer une séquence |
\n
|
référence | Même séquence que celle capturée précédemment par le nème groupe de capture |
\g{n}
|
référence | Même séquence que celle capturée précédemment par le nème groupe de capture |
(?P<nom>pattern)
|
Sous-motif nommé | Nomme le résultat d'un groupe de capture par un nom. |
\g{nom}
|
référence | Même séquence que celle capturée précédemment par le groupe de capture nommé nom. |
Par défaut, les caractères et groupes ne sont pas répétés. Les quantificateurs permettent de spécifier le nombre de répétitions et sont spécifiés immédiatement après le caractère ou groupe concerné.
Caractère | Type | Explication |
---|---|---|
*
|
astérisque | 0, 1 ou plusieurs occurrences |
+
|
plus | 1 ou plusieurs occurrences |
?
|
interrogation | 0 ou 1 occurrence |
{...}
|
accolades | nombre de répétitions : spécifie le nombre de répétitions du motif précédent (minimum et maximum). Avec la présence de la virgule, quand le minimum est absent la valeur par défaut est zéro, quand le maximum est absent la valeur pas défaut est l'infini. Sans virgule (un seul nombre) il s'agit du nombre exact (minimum et maximum ont la même valeur). Exemples :
|
Par défaut les quantificateurs ne recherchent pas forcément la plus longue séquence de répétition possible. Il est possible de les suffixer avec un caractère pour modifier leur comportement.
Caractère | Type | Explication |
---|---|---|
?
|
réticent | Le quantificateur qui précède recherchera la plus petite séquence possible. |
+
|
possessif | Le quantificateur qui précède recherchera la plus grande séquence possible. |
Remarques :
- Les caractères de début et fin de chaîne (
^
et$
) ne fonctionnent pas dans[]
où ils ont un autre rôle. - Les opérateurs
*
et+
sont toujours avides, pour qu'ils laissent la priorité il faut leur apposer un?
à leur suite[2].
Classe | Signification |
---|---|
[[:alpha:]]
|
n'importe quelle lettre |
[[:digit:]]
|
n'importe quel chiffre |
[[:xdigit:]]
|
caractères hexadécimaux |
[[:alnum:]]
|
n'importe quelle lettre ou chiffre |
[[:space:]]
|
n'importe quel espace blanc |
[[:punct:]]
|
n'importe quel signe de ponctuation |
[[:lower:]]
|
n'importe quelle lettre en minuscule |
[[:upper:]]
|
n'importe quelle lettre capitale |
[[:blank:]]
|
espace ou tabulation |
[[:graph:]]
|
caractères affichables et imprimables |
[[:cntrl:]]
|
caractères d'échappement |
[[:print:]]
|
caractères imprimables exceptés ceux de contrôle |
Expression | Signification |
---|---|
\\ |
Antislash |
\C |
Caractère spécial C non interprété : [ ] { } ( ) ? * . : \ & - ^ $
|
\Q...\E |
Séquence littérale non interprétée |
\0xxx |
Caractère Unicode (1 à 3 chiffres octaux) |
\a |
Alarme (ASCII 07) |
\A |
Début de chaîne |
\b |
Caractère de début ou fin de mot |
\B |
Caractère qui n'est pas début ou fin de mot |
\cX |
Caractère de contrôle ASCII (X étant une lettre) |
\d |
Chiffre |
\D |
Non chiffre |
\e |
Escape (ASCII 1B) |
\f |
Form-feed (ASCII 0C) |
\G |
Fin de la correspondance précédente |
\h |
Espace blanc horizontal [ \t\xA0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000]
|
\H |
Non espace blanc horizontal [^\h]
|
\n |
Fin de ligne |
\pL , \p{L} , \p{Letter} |
Lettre (dans tout langage) |
\r |
Retour charriot |
\R |
Retour à la ligne, équivaut à \u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]
|
\s |
Caractères espace [ \t\n\x0B\f\r]
|
\S |
Non caractères espace [^\s]
|
\t |
Tabulation |
\v |
Espace blanc vertical [\n\x0B\f\r\x85\u2028\u2029]
|
\V |
Non espace blanc vertical [^\v]
|
\w |
Caractère alphanumérique : lettre, chiffre ou underscore |
\W |
Caractère qui n'est pas lettre, chiffre ou underscore |
\xxx |
Caractère Unicode (2 chiffres hexadécimaux) |
\x{xx...x} |
Caractère Unicode (chiffres hexadécimaux) |
\X |
Caractère Unicode du groupe de graphèmes étendu |
\z |
Fin de chaîne |
Constructeurs spéciaux : Ces fonctions précèdent l'expression à laquelle elles s'appliquent, et le tout doit être placé entre parenthèses.
?:
: groupe non capturant. Ignorer le groupe de capture lors de la numérotation des backreferences. Exemple :((?:sous-chaine_non_renvoyée|autre).*)
.- La présence d'un groupe capturant peut engendrer une allocation mémoire supplémentaire. Si une expression régulière particulièrement complexe provoque une erreur de mémoire, essayez de remplacer les groupes capturant non référencés et inutilisés par des groupes non-capturant en ajoutant
?:
juste après la parenthèse ouvrante, et en décalant les numéros des groupes référencés.
- La présence d'un groupe capturant peut engendrer une allocation mémoire supplémentaire. Si une expression régulière particulièrement complexe provoque une erreur de mémoire, essayez de remplacer les groupes capturant non référencés et inutilisés par des groupes non-capturant en ajoutant
?>
: groupe non capturant indépendant.?<=
: positive lookbehind, vérifier (sans consommer) que ce qui précède correspond au motif spécifié. Exemple :- Chercher une lettre u précédée d'une lettre q :
(?<=q)u
- Chercher une lettre u précédée d'une lettre q :
?<!
: negative lookbehind, vérifier (sans consommer) que ce qui précède ne correspond pas au motif spécifié.?=
: positive lookahead, vérifier (sans consommer) que ce qui suit correspond au motif spécifié.?!
: negative lookahead, vérifier (sans consommer) que ce qui suit ne correspond pas au motif spécifié. Exemples :
Options :
Les options d'interprétation sont en général spécifiées à part. Mais certaines API ne permettent pas de les spécifier. Il est possible d'insérer ces options dans l'expression régulière[7].
(?optionsactivées-optionsdésactivées)
Exemples :
- Chercher un mot composé de voyelles sans tenir compte de la casse :
(?i)[AEIOUY]+
- Chercher un mot composé de voyelles en tenant compte de la casse, ici en majuscules :
(?-i)[AEIOUY]+
Les options s'appliquent à toute l'expression quelle que soit leur position dans l'expression.
Nous allons faire un tour des fonctionnalités (que j'appellerai également modes) les plus utilisés.
recherche : m
[modifier | modifier le wikicode]Pour effectuer des recherches dans une chaîne de caractère, nous utilisons la fonctionnalité m. C'est le mode par défaut, nous n'avons pas besoin de l'écrire.
#!/usr/bin/perl -w
use strict;
# ^ début de ligne
@tableau = ("affiche", "âme", "à toi", " affiche");
print 'Test sur la regex : if ( $texte =~ m/^a/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/^a/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "(espace en-tête)";
print "\n" x 2;
# $ fin de ligne
@tableau = ("affiche", "affiche.", "affiches");
print 'Test sur la regex : if ( $texte =~ m/che.$/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/che.$/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n" x 2;
# \. point échappé
@tableau = ("affiche", "affiche.", "affiches");
print 'Test sur la regex : if ( $texte =~ m/che\.$/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/che\.$/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print '(\. point échappé = vrai point, pas n‘importe quel caractère)';
print "\n" x 2;
# {…}
@tableau = qw(tttrès hutte haute hausse tousse);
print 'Test sur la regex : if ( $texte =~ m/[t]{1,2}/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/t{2,3}/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
remplacement : s
[modifier | modifier le wikicode]Le mode s permet d'effectuer un remplacement (aussi appelé substitution) dans une chaîne.
Ici, nous avons besoin d'utiliser les deux zones de textes, de cette manière : s/texte à changer/nouveau texte/
.
exemple de substitution
[modifier | modifier le wikicode]my $texte = "J'aime le fromage.";
$texte =~ s/le fromage/les légumes/; # les légumes c'est plus sain que le fromage !
say $texte; # affiche « J'aime les légumes. »
stocker une expression régulière : qr
[modifier | modifier le wikicode]my $regex_mail = qr{[\w-+.]+@[\w-]+(?:\.[\w-]+)+};
unless ("nom@example.com" =~ /$regex_mail/) {
say "Ce n'est pas une adresse mail";
}
constructeurs spéciaux :
[modifier | modifier le wikicode]?= préviseur positif (positive lookahead)
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
my $texte = "anticonstitutionnellement";
# On cherche un "ti" qui précède ("il prévoit") "on" (suite affichée pour la clarté)
$texte =~ /(ti(?=on).+)/;#
print $1."\n"; # tionnellement
# un "ti" qui précède "tu"
$texte =~ /(ti(?=tu).+)/;#
print $1."\n"; # titutionnellement
?! préviseur négatif (negative lookahead)
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
my $texte = "anticonstitutionnellement";
# On cherche un "ti" qui NE précède PAS "on" (suite affichée pour la clarté)
$texte =~ /(ti(?!on).+)/;#
print $1."\n"; # ticonstitutionnellement
# un "ti" qui NE précède PAS "cons"
$texte =~ /(ti(?!cons).+)/;#
print $1."\n"; # titutionnellement
?<= rétroviseur positif (positive lookbehind)
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
$texte = "anticonstitutionnellement";
# On cherche un "ti" qui suit "tu" (début affiché pour la clarté)
$texte =~ /(.+(?<=tu)ti)/;#
print $1."\n"; # anticonstituti
# un "ti" qui suit "an"
$texte =~ /(.+(?<=an)ti)/;#
print $1."\n"; # anti
?<! rétroviseur négatif (negative lookbehind)
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
$texte = "anticonstitutionnellement";
# On cherche un "ti" qui NE suit PAS "tu" (début affiché pour la clarté)
$texte =~ /(.+(?<!tu)ti)/;#
print $1."\n"; # anticonsti
# un "ti" qui NE suit PAS "an"
$texte =~ /(.+(?<!an)ti)/;#
print $1."\n"; # anticonstituti
?> constructeur tête de mule !
[modifier | modifier le wikicode]Attrape la chaîne correspondante maximale où il est ancré, et refuse de faire un pas en arrière
( pas de backtracking pour trouver toutes les sous-chaînes ).
#!/usr/bin/perl -w
use strict;
my $count=0;
my $texte="a"x 33 ."b";
# on cherche les séries a…ab & les sous-séries a…a
# sans ?>
$texte =~ /(a+b?)(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n"; # 594
# avec ?>
$count=0;
$texte =~ /(?>(a+b?))(?{print "$&\n"; $count++})(*FAIL)/;
print "Count=$count\n"; # 33
Sert à l'optimisation :
Pour 2 000 "a", on attend 2 003 000 recherches (sans ?>) contre 2 000 seulement (avec ?>).
Correspondances
[modifier | modifier le wikicode]Lors d'une recherche de motif, nous pouvons utiliser des caractères spéciaux (parfois appelés « méta-caractères ») pour rechercher un type de caractère (et non un caractère explicitement).
caractères et expressions de correspondance
[modifier | modifier le wikicode]caractère quelconque : .
[modifier | modifier le wikicode]Le point « . » correspond à un caractère quelconque. Lors d'une recherche, nous pouvons l'utiliser pour indiquer n'importe quel caractère, excepté le retour à la ligne (\n).
exemple utilisation du point
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
# le point .
my @tableau = qw(matin mâtin méthode admit);
print 'Test sur la regex : if ( $texte =~ m/m.t/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/m.t/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "(lettres accentuées = 2 octets)";
print "\n" x 2;
# double point ..
@tableau = qw(matin mâtin méthode admit);
print 'Test sur la regex : if ( $texte =~ m/m..t/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/m..t/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
caractère alphabétique : [[:alpha:]]
[modifier | modifier le wikicode]Pour rechercher un caractère alphabétique on utilise: [[:alpha:]] ou éventuellement [a-zA-Z].
#!/usr/bin/perl -w
use strict;
# [[:alpha:]]
my @tableau = qw(satin 264 mâtin àâéèêëîôùûæœç àâéèêëîôùûæœça);
print 'regex : if ( $texte =~ m/[[:alpha:]]/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/[[:alpha:]]/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print '(voyelles accentuées non ASCII)';
print "\n";
caractère numérique : \d et [[:digit:]]
[modifier | modifier le wikicode]Nous avons deux possibilités pour rechercher un caractère numérique : [[:digit:]] et \d. Pour chercher un caractère qui n'est pas un chiffre : \D.
#!/usr/bin/perl -w
use strict;
# [[:digit:]]
my @tableau = qw(satin 264 37.5lematin àâ68éèêëîôùûæœç);
print 'regex : if ( $texte =~ m/[[:digit:]]/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/[[:digit:]]/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
caractère hexadécimal : [[:xdigit:]]
[modifier | modifier le wikicode]Cela correspond à tout caractère hexadécimal, c'est à dire tous les caractères numériques (de 0 à 9) et les lettres de A à F.
#!/usr/bin/perl -w
use strict;
# [[:xdigit:]]
my @tableau = qw(sAtin DEBAClE 0123456789ABCDEF abcdef ghijkl);
print 'regex : if ( $texte =~ m/[[:xdigit:]]/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/[[:xdigit:]]/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
caractère alphanumérique : [[:alnum:]] et \w
[modifier | modifier le wikicode]Les caractères alphanumériques correspondent à l'ensemble des caractères numériques (0,1,2…) et aux lettres. La classe \w reconnaît, en plus de [[:alnum:]], le caractère «_». Pour chercher un caractère qui n'est pas un nombre ou une lettre ou «_» : \W.
#!/usr/bin/perl -w
use strict;
# non [[:alnum:]] = \W
my @tableau = qw(&§@$£()[]{}#°%_ sAtin 0123456789ABCDEF §ghijkl);
print 'regex : if ( $texte =~ m/\W/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/\W/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
caractère d'espacement simple : [[:blank:]]
[modifier | modifier le wikicode]Un espacement correspond à un espace, une tabulation.
#!/usr/bin/perl -w
use strict;
# [[:blank:]]
my @tableau = ( "ça va?", "tab ulation","tabulation"," vu!");
print 'regex : if ( $texte =~ m/[[:blank:]]/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/[[:blank:]]/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
caractère d'espacement quelconque : \s et [[:space:]]
[modifier | modifier le wikicode]On cherche ici un espacement tel qu'une tabulation, un espace, un saut de ligne ou de page. Pour chercher un caractère qui n'est pas un espacement : \S.
#!/usr/bin/perl -w
use strict;
# \S = non [[:blank:]]
my @tableau = ( " ", " ","t "," v");
print 'regex : if ( $texte =~ m/\S/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/\S/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
lettre en minuscule : [[:lower:]]
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
# [[:lower:]]
my @tableau = qw( ABCDE ABCdE);
print 'regex : if ( $texte =~ m/[[:lower:]]/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/[[:lower:]]/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
lettre en majuscule : [[:upper:]]
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
# [[:upper:]]
my @tableau = qw( abcde abcDe);
print 'regex : if ( $texte =~ m/[[:upper:]]/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/[[:upper:]]/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
caractère de ponctuation : [[:punct:]]
[modifier | modifier le wikicode]correspondances partielles : []
[modifier | modifier le wikicode]Nous cherchons une correspondance avec uniquement une partie des caractères, nous utilisons alors []. Nous pouvons choisir d'énumérer explicitement tous les caractères dont nous souhaitons vérifier la correspondance en les écrivant entre ces crochets. Un moyen plus répandu (lorsque cela est possible) est d'utiliser une suite de caractères, par exemple de « a » à « z » de cette manière : [a-z]. Pour correspondre avec l'inverse de ce qu'on note entre crochets, il suffit de mettre en première lettre « ^ ». Ainsi, la recherche inverse de l'exemple précédent devient : [^a-z].
exemple de correspondance partielle
[modifier | modifier le wikicode]Cherchons à faire correspondre uniquement les caractères de « a » à « e » en minuscule, de « G » à « L » en majuscule et les chiffres de « 0 » à « 5 » et les chiffres 7 et 9.
$texte =~ /[a-eG-K0-579]/;
crochets []
#!/usr/bin/perl -w
use strict;
# crochets []
@tableau = qw(affiche affole affuble affriole effectué effacer);
print 'Test sur la regex : if ( $texte =~ m/ff[a-df-np-s]/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/ff[a-df-np-s]/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
crochets [^…]
#!/usr/bin/perl -w
use strict;
# crochets [^…]
@tableau = qw(affiche affole affuble affriole effectué effacer);
print 'Test sur la regex : if ( $texte =~ m/ff[^ieo]/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/ff[^ieo]/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
Options
[modifier | modifier le wikicode]ignorer la casse
[modifier | modifier le wikicode]La « casse » désigne en informatique la différence entre minuscule et majuscule. Si on choisi d'ignorer la casse, alors nous recherchons un motif sans distinction avec ou sans majuscules. Pour cela nous avons l'option i.
recherche en mode multi-ligne
[modifier | modifier le wikicode]Nous recherchons un motif en traitant la chaîne en tant que constituée de plusieurs lignes. Concrètement, nous voulons que ^ et $ ne correspondent pas seulement au début et fin de la chaîne, mais aussi au début et fin de chaque ligne. Pour cela, nous avons l'option m.
exemple de recherche en mode multi-ligne
[modifier | modifier le wikicode]my $texte = "Ce texte
est sur
plusieurs lignes";
# utilisation de l'option m dans une recherche (fonctionnalité de recherche m)
if( $texte =~ m/sur$/m )
{
say "On a trouvé « sur » en fin de ligne";
}
else
{
say "On n'a pas trouvé « sur » en fin de ligne";
}
faire correspondre . à un saut de ligne
[modifier | modifier le wikicode]Lorsque nous faisons des recherches sur plusieurs lignes, on souhaiterait pouvoir considérer le saut de ligne comme un caractère quelconque (comme tous les autres). Nous pouvons faire cela avec l'option s.
exemple utilisation de l'option s
[modifier | modifier le wikicode]Nous reprenons l'exemple précédent avec la recherche sur plusieurs lignes, désormais nous n'avons pas à écrire explicitement le saut de ligne, nous pouvons utiliser le métacaractère ..
my $texte = "Ce texte
est sur
plusieurs lignes";
if( $texte =~ m/sur.plusieurs/s )
{
say "On a trouvé « sur plusieurs » dans le texte";
}
else
{
say "On n'a pas trouvé « sur plusieurs »";
}
permettre d'écrire l'expression régulière avec des commentaires
[modifier | modifier le wikicode]L'option « x » permet d'écrire une expression régulière sans tenir compte des caractères d'espacement. Ainsi, on peut espacer ses expressions avec des retours à la ligne si on veut, sans que cela n'influe sur votre expression. Si vous souhaitez rechercher une chaîne de caractères avec des espaces à certains endroits, il faudra l'indiquer explicitement dans votre expression avec la recherche d'un caractère d'espacement (voir plus haut).
appliquer un changement plusieurs fois dans la chaîne
[modifier | modifier le wikicode]Lors de la substitution de texte, nous souhaiterions par exemple changer tous les caractères « a » par « b ». Par défaut, seule la première occurrence de « a » serait modifiée. Pour changer ce comportement et changer tous les « a », nous utilisons l'option g.
#!/usr/bin/perl -w
use strict;
# remplacement simple
my $texte = "affuble mutin hure mure fumé luit";
print $texte."\n";
$texte =~ s/u/a/;
print $texte;
print "\n" x 2;
# remplacement multiple
$texte = "affuble mutin hure mure fumé luit";
print $texte."\n";
$texte =~ s/u/a/g ;
print $texte;
print "\n";
cumuler les options
[modifier | modifier le wikicode]Nous pouvons additionner les options en les écrivant à la suite (/ig par exemple). Nous avons déjà vu un exemple plus haut en cumulant les options m (recherche de motif sur plusieurs lignes) et s (retour chariot inclut dans les caractères auxquels le point correspond). Autre exemple, nous pouvons effectuer une substitution plusieurs fois par ligne en ignorant la casse du motif à remplacer.
Ancres
[modifier | modifier le wikicode]Les ancres correspondent au placement du texte à rechercher.
rechercher en début de chaîne : ^ et \A
[modifier | modifier le wikicode]On utilise pour cela le caractère ^ ou \A qui correspond au début de la chaîne. Avec l'option /m le caractère ^ correspond également à tout début de ligne (voir plus haut).
exemple de recherche en début de chaîne
[modifier | modifier le wikicode]my $texte = "Hello world!";
$texte =~ /world/ ; # correspond
$texte =~ /^world/ ; # ne correspond pas car « world » n'est pas au début de la chaîne
^ début de ligne
#!/usr/bin/perl -w
use strict;
# ^ début de ligne
@tableau = ("affiche", "âme", "à toi", " affiche");
print 'regex : if ( $texte =~ m/^a/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/^a/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "(espace en-tête)";
print "\n";
rechercher en fin de chaîne : $, \Z et \z
[modifier | modifier le wikicode]$ est l'analogue de ^ mais en fin de chaîne (ou de ligne aussi avec option m). \z est l'analogue de A en fin de chaîne, donc non affecté par l'option m. \Z est comme \z mais peut correspondre aussi à un retour à la ligne en fin de chaîne.
exemple de recherche en fin de chaîne
[modifier | modifier le wikicode]my $texte = "Hello world!";
$texte =~ /Hello/ ; # correspond
$texte =~ /Hello$/ ; # ne correspond pas car « Hello » n'est pas en fin de chaîne
$ fin de ligne
#!/usr/bin/perl -w
use strict;
# $ fin de ligne
@tableau = ("affiche", "affiche.", "affiches");
print 'regex : if ( $texte =~ m/che.$/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/che.$/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
Quantification
[modifier | modifier le wikicode]Nous allons aborder maintenant la partie concernant l'identification d'un motif se répétant.
exemple d'introduction au quantifications
[modifier | modifier le wikicode]Avant de commencer, il faut se confronter au problème. Nous avons une chaîne de caractères, et nous souhaitons savoir si elle contient deux fois le caractère « a ». Vu l'étendue des connaissances acquises jusque-là, nous devrions résoudre le problème comme suit :
$texte =~ /aa/; # si $texte contient 2 fois le caractère « a » il correspondra
Maintenant, on souhaite savoir si la chaîne de caractères contient deux fois le caractère « a » (jusque-là, le problème reste identique) sauf que ces deux caractères peuvent être séparés par un caractère quelconque (ou pas). Là encore, vu l'étendue des connaissances acquises actuellement, cela donnerait :
if( $texte =~ /aa/ || $texte =~ /a.a/) # pas de caractère séparateur ou un seul
{
... # si ça correspond, on fait quelque chose
}
Nous résolvons le problème ici assez facilement, mais qu'en est-il si nous cherchons deux fois le caractère « a » avec un nombre quelconque de caractères séparateurs ? Impossible actuellement.
Quantifier par un nombre : {}
[modifier | modifier le wikicode]Nous avons une chaîne de caractères, et nous recherchons un motif quelconque se répétant un certain nombre de fois. Nous pouvons expliciter ce nombre de fois, ou simplement donner une tranche :
* de x à y fois : {x,y} * au moins x fois : {x,}
exemples avec : {}
[modifier | modifier le wikicode]my $texte = "coucou!";
$texte =~ /c.{2}c/ ; # correspond
$texte =~ /c.{1,3}c/ ; # correspond
$texte =~ /cou{2}/ ; # ne correspond pas !
$texte =~ /c.{1,}c/ ; # correspond
Ligne 2 : il y a correspondance car nous avons précisément deux caractères séparant les deux lettres « c ». Ligne 3 : il y a correspondance là aussi, car il y a un nombre compris entre 1 et 3 caractères entre les deux lettres « c ». Attention : ligne 4 ne correspond pas car il faudrait qu'il y ait 2 lettres « u » consécutives et non toute la chaîne « cou » ! Nous verrons plus tard comment procéder pour faire cela. Enfin, ligne 5 correspond car on cherche « au moins » un caractère entre deux « c ».
Entre 0 ou 1 occurrence : ?
[modifier | modifier le wikicode]Il est possible que le caractère (ou plus généralement le motif) soit présent ou non. On utilise pour cela le quantificateur ?.
#!/usr/bin/perl -w
use strict;
# quantificateur ?
@tableau = qw(pommes pomme mess);
print 'Test sur la regex : if ( $texte =~ m/me[s]?$/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/me[s]?$/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
Entre 0 ou un nombre non déterminé d'occurrences : *
[modifier | modifier le wikicode]Quand il peut y avoir 0 ou une infinité d'occurrences d'un motif, on utilise le caractère spécial * .
#!/usr/bin/perl -w
use strict;
# *
@tableau = qw(miss mess messe messes);
print 'Test sur la regex : if ( $texte =~ m/e[s]*$/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/e[s]*$/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n" x 2;
# +
@tableau = qw(pomme pommes mess messe messes);
print 'Test sur la regex : if ( $texte =~ m/e[s]+$/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/e[s]+$/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
Capturer du texte
[modifier | modifier le wikicode]La capture du texte (d'un certain motif) permet de réutiliser ce texte pour le replacer (en le modifiant éventuellement).
Simple capture de texte : ()
[modifier | modifier le wikicode]Nous souhaitons prendre une partie du texte pour le réutiliser. Lorsque vous capturez du texte, les variables $1, $2 … sont mises à jour avec la capture que vous venez de faire entre parenthèses. Ainsi, le premier groupe capturé par () pourra être réutilisé dans votre code via la variable $1, le second avec la variable $2 et ainsi de suite jusqu'à $9.
#!/usr/bin/perl -w
use strict;
# groupe de capture () avec antéréférence
@tableau = qw(dare-dare borborigme calcul barbare Tartare bébé );
print 'Test sur la regex : if ( $texte =~ m/(...)\1/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/(...)\1/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "(é = 2 octets)";
print "\n";
Ne pas capturer le motif : (?:)
[modifier | modifier le wikicode]my $texte = "J'aime le fromage";
$texte =~ m/(?:aime).*(fromage)/;
say $1; # affiche "fromage"
#!/usr/bin/perl -w
use strict;
# non capture des 6 radicaux, réutilisation de 12 motifs sujet-suffixe
my $texte = "je chante, tu chantes, il chante, nous chantons, vous chantez, ils chantent";
print $texte."\n";
$texte =~ s/(\w+) (?:\w+)(\w), (\w+) (?:\w+)(\w{2}), (\w+) (?:\w+)(\w), (\w+) (?:\w+)(\w{3}), (\w+) (?:\w+)(\w{2}), (\w+) (?:\w+)(\w{3})/$1_$2 $3_$4 $5_$6 $7_$8 $9_$10 $11_$12/;
print $texte."\n";
print "\n" x 2;
Réutiliser les motifs capturés : \1 \2 …
[modifier | modifier le wikicode]my $texte = "J'aime le le fromage"; # doublon «le»
$texte =~ s/(\w+) \1/$1/g;
# $texte contient "J'aime le fromage" sans doublon.
# non capture du 3e, antéréférence au 6e, réutilisation de 8 motifs
my $texte = "chanter - verbe du 1e groupe - e es e ons ez ent";
print $texte."\n";
$texte =~ s/(\w+)(..) (?:-.*-) (\w+) (\w+) \3 (\w+) (\w+) (\w+)/je $1$3, tu $1$4, il $1$3, nous $1$5, vous $1$6, ils $1$7./;
print $texte."\n";
print "\n";
Définir des alternatives : (a|b)
[modifier | modifier le wikicode]#!/usr/bin/perl -w
use strict;
# "ou" inclusif |
my @tableau = qw(satin malin matin calin);
print 'regex : if ( $texte =~ m/m|t/ )'."\n";
foreach my $texte (@tableau){
print "“$texte“";
if ( $texte =~ m/m|t/ ){ print " vrai, "; }
else{ print " faux, "; }
}
print "\n";
Captures avides ou non avides
[modifier | modifier le wikicode]Le plus simple pour comprendre ce qu'est une capture avide est de prendre un exemple.
capture avide
[modifier | modifier le wikicode]exemple 1 de capture avide
[modifier | modifier le wikicode]Soit la capture « (c+) » qui va prendre une fois ou un nombre non limité de fois la lettre « c ».
$texte = "cccccc";
$texte =~ /^(c+)/;
say $1;
Si vous exécutez ce code, vous verrez affiché « cccccc » et non pas un seul « c ». Cela est dû à l'avidité (en anglais greedy) des opérateurs.
exemple 2 de capture avide
[modifier | modifier le wikicode]Un autre exemple, un peu plus complexe, pour bien comprendre le phénomène, qui peut s'avérer gênant : nous avons une chaîne de caractères avec des champs délimités par des doubles points « : » :
$texte = "nom:prenom:age:date/de/naissance";
$texte =~ /^(.+):/; # .+ : on récupère le maximum de caractères avant de tomber sur un double points
say $1;
Ici, nous affichons le nom, le prénom et l'âge, car nous prenons la chaîne la plus longue possible correspondant à notre expression. C'est à dire, la chaîne la plus longue se terminant par un double point.
capture non avide
[modifier | modifier le wikicode]Maintenant, ce qu'on voudrait c'est de ne prendre que la plus petite partie possible qui correspond à notre expression régulière. La solution est de remplacer chaque quantificateur par sa version non avide :
- , +, ?, et {} deviennent respectivement *?, +?, ?? et {}?.
#!/usr/bin/perl -w
use strict;
# + (ou *) avide
my $texte = "anticonstitutionnellement";
$texte =~ /^(.+)i/;
print $1."\n"; # anticonstitut
# +? (ou *?) sobre
$texte =~ /^(.+?)i/;
print $1."\n"; # ant
# {…} avide
$texte =~ /^(.{6,})e/;
print $1."\n"; # anticonstitutionnellem
# {…}? sobre
$texte =~ /^(.{6,}?)e/;
print $1."\n"; # anticonstitutionn
Les coulisses des regex
[modifier | modifier le wikicode]@+ et @- les @rchiveurs de regex
[modifier | modifier le wikicode]@+ matrice des fins des chaînes trouvées ($+[0] fin globale, $+[1] fin de $1 …)
@- matrice des débuts des chaînes
Les items de @+ ne se notent pas @+[0], @+[1] … mais $+[0], $+[1] … car ce sont des scalaires.
#!/usr/bin/perl -w
use strict;
my $texte = "anticonstitutionnellement";
$texte =~ m/(cons....).*(nne).*(ll)/;
# les 3 correspondances (matchings) trouvées
print "$1 $2 $3\n" ; # constitu nne ll
# traces de la dernière recherche (double "l")
print substr($texte,0,$-[0])." ".substr($texte,$-[0],$+[0]-$-[0])." ".substr($texte, $+[0])."\n";
# print substr($texte,0,4) substr($texte,4,16) substr($texte, 20)
# anti constitutionnell ement
# avant chaîne trouvée après
# variable $2
print $-[2]." ".$+[2]."\n"; # 15 18 : début & fin de "nne"
# @+ matrice des fins des chaînes $0 $1 $2 $3
print "@+\n"; # 20 12 18 20
# @- matrice des débuts des chaînes
print "@-\n"; # 4 4 15 18
Orienté Objet
Introduction
[modifier | modifier le wikicode]Cette page sera écrite en deux parties distinctes : la définition de la programmation orientée objet en faisant abstraction d'un langage en particulier, puis comment l'utiliser dans le langage étudié.
Gardez à l'esprit que le concept de programmation orientée objet est simple, il y a juste quelques mots de jargon à connaître.
Ce qu'est la programmation orientée objet
[modifier | modifier le wikicode]La programmation orientée objet est une manière de concevoir une application. La page wikipedia à ce sujet est bien faite, mais faisons un petit résumé pratique (et plus clair).
La POO se base sur quelques notions assez simples (rappelons que le but est de simplifier la programmation).
Les objets et leur classe
[modifier | modifier le wikicode]Un objet est un ensemble d'attributs (variables) et de méthodes (fonctions). Une voiture par exemple pourrait se représenter par un objet « Voiture ». Elle est composée de quatre roues, un volant, quatre sièges… qui représentent autant d'attributs de l'objet. La voiture peut également se démarrer, rouler, tourner, s'arrêter… qui représentent les méthodes possibles pour cet objet.
Dans la plupart des langages, pour créer un objet et le définir, nous créons une classe (un fichier avec les différents attributs et méthodes de l'objet). À partir de cette classe, nous pouvons créer des instances. Ainsi, nous pouvons avoir autant de voitures que nous le souhaitons, qui possèdent toutes les mêmes attributs (pas forcément de même valeur cependant) et les mêmes méthodes, à partir de la classe voiture. Exemple : la classe voiture possède un attribut « couleur » et des méthodes (rouler, tourner…), nous créons deux instances de la classe voiture, une avec l'attribut « couleur » à « rouge » et l'autre à « bleue ». Les voitures sont indépendantes.
Le typage et le polymorphisme
[modifier | modifier le wikicode]Chaque objet est un type différent (le type est le nom de sa classe). Il définit ses propres attributs et ses méthodes, et il est utile d'un point de vue sémantique dans notre code. Prenons par exemple la gestion d'un parking, on va faire entrer des « Voitures » dans ce parking, pas des entiers ou une chaîne de caractères. C'est donc une structure de données qui a un but sémantique.
Héritage et redéfinition
[modifier | modifier le wikicode]Pour simplifier l'écriture de code (et en écrire moins), on a créé le principe d'héritage. Prenons un exemple simple : nous souhaitons gérer un jeu de courses de voitures. Nous possédons une trentaines de modèles de voitures différentes (quelques particularités qui changent), cependant elles ont toutes quatre roues, des portières etc. Pour simplifier l'écriture du code, nous allons définir ce qu'est une voiture, et dire que les différents modèles « héritent » de la classe « voiture ». Ainsi, tous les modèles possèdent de base les attributs qu'ils ont en commun, sans avoir à le dire explicitement dans chaque classe. On vient donc de factoriser grandement notre code.
Perl et la POO
[modifier | modifier le wikicode]Perl a connu plusieurs implémentations de programmation orientée objet via différents modules. Afin d'harmoniser les pratiques, Perl6 reprend le concept de POO dans sa syntaxe de base, et ceci a été repris dans Perl5 par le module Moose. Nous allons voir comment programmer en orienté objet avec cette bibliothèque, qui s'avère de nos jours être la bonne manière de coder en POO en Perl.
Bases de POO en Perl
[modifier | modifier le wikicode]Créer une instance : new
[modifier | modifier le wikicode]Chaque package possède implicitement une méthode particulière (souvent appelée « new ») pour créer une instance de la classe. Nous appelons les méthodes d'un objet via l'opérateur « -> ».
use Voiture;
my $objet = Voiture->new(); # Appel d'une méthode avec l'opérateur « -> », lié à la POO
$objet est maintenant une référence à un objet « Voiture ».
Connaître la classe d'un objet : ref
[modifier | modifier le wikicode]use Voiture;
my $objet = Voiture->new(); # Exemple précédent
say ref($objet); # Affiche « Voiture »
Créer et appeler une méthode : sub et ->
[modifier | modifier le wikicode]Une méthode est une simple fonction comme nous avons déjà vu dans un chapitre précédent. Le premier paramètre est une référence à l'objet courant. Cette méthode est définie dans un « package », qui sera notre classe. Nous verrons plus loin comment créer une classe, pour voir directement la bonne manière de faire (via Moose).
Comme nous l'avons vu précédemment avec « new », pour appeler une méthode il suffit d'utiliser l'opérateur « -> ».
use Voiture; # on indique qu'on utilise la classe Voiture
my $voiture = Voiture->new(); # on crée une instance
$voiture->demarrer(); # démarre la voiture référencée par $voiture
À remarquer : au début on utilise l'opérateur « -> » sur le package directement pour créer l'instance, après on l'utilise sur l'objet référençant l'instance pour faire appel aux méthodes liées à un objet en particulier (que l'on nomme méthodes d'instance).
POO via Moose
[modifier | modifier le wikicode]Nous arrivons à la partie la plus intéressante. Jusque-là tout était peut-être un peu flou, mais cela va s'éclaircir !
Créer une classe
[modifier | modifier le wikicode]Une classe se définit par un package, l'utilisation de Moose et se termine par « 1; » tout à la fin du fichier.
#!/usr/bin/env perl
package Voiture; # on crée la classe « Voiture »
use Moose; # Utilisation de Moose pour créer une classe
1; # Permet de s'assurer qu'une classe a bien été chargée
Difficile de faire plus simple non ? Cette classe ne contient rien de bien utile pour le moment, on ne peut que créer une instance de l'objet comme vu précédemment.
Les attributs
[modifier | modifier le wikicode]Bases : has is isa required
[modifier | modifier le wikicode]Un attribut est déclaré avec le mot clé has. On doit lui passer au moins un paramètre : l'option « is » qui définit si l'attribut est en lecture seule (ro pour read-only) ou s'il est en lecture et écriture (rw pour read-write).
has couleur => ( is => 'rw');
Les attributs peuvent avoir un type précis, qui se trouve dans [liste]. Nous forçons à vérifier ce type avec l'option isa.
has force => ( is => 'rw', isa => 'Int');
Nous pouvons même créer de nouveaux types mais cela va au delà du but de ce livre, veuillez vous référer à la documentation officielle du module MooseUtilTypeConstraint.
Nous pouvons décider qu'une option est obligatoire.
has couleur => ( is => 'rw', required => 1 );
Il y a d'autres options disponibles dont de très intéressantes et puissantes à découvrir dans la documentation du module [[1]], que j'encourage vivement à aller voir pour les anglophones.
Déclarer plusieurs attributs directement
[modifier | modifier le wikicode]Petite astuce pour gagner du temps, déclarer plusieurs attributs avec les mêmes options.
has [ 'force', 'vitesse', 'endurance' ] => ( is => 'rw', isa => 'Int' ); # déclare 3 attributs de type entier en lecture et écriture
Créer l'instance en modifiant la valeur des attributs
[modifier | modifier le wikicode]On peut choisir de modifier la valeur d'un attribut dès la création de l'instance de la classe.
my $voiture = Voiture->new(couleur => "rouge"); # l'attribut "couleur" vaut maintenant "rouge"
Récupérer et modifier la valeur d'un attribut
[modifier | modifier le wikicode]Pour récupérer ou modifier un attribut, il faut passer par des fonctions nommées accesseurs. Pour récupérer la valeur d'un attribut il suffit d'écrire $objet->nom_attribut et pour la modifier $objet->nom_attribut("nouvelle valeur").
my $voiture = Voiture->new(couleur => "rouge"); # l'attribut "couleur" vaut maintenant "rouge"
my $couleur_voiture = $voiture->couleur; # on récupère l'attribut "couleur" de l'instance de la classe "Voiture"
say $couleur_voiture; # affichera "rouge"
$voiture->couleur("bleue"); # on modifie l'attribut "couleur"
$couleur_voiture = $voiture->couleur; # on récupère à nouveau l'attribut (qui a été modifié)
say $couleur_voiture; # affichera "bleue"
Modifier les noms des accesseurs
[modifier | modifier le wikicode]Il est possible de modifier les fonctions d'accès et de modification des attributs. Cela se fait grâce aux options reader et writer de la création d'un attribut.
package Personnage; # on crée la classe « Personnage »
# dans cet exemple, nous modifions le nom des deux fonctions permettant l'accès en lecture et en écriture de l'attribut "nom"
has nom => ( is => rw, reader => 'obtenir_nom_personnage', writer => 'renommer_personnage' );
Une convention veut que nous nous précédions le nom de l'accesseur par un underscore ( _ ) lorsque nous ne voulons pas que les classes filles l'utilisent. Rien n'empêche en pratique leur utilisation, mais cela est à utiliser à vos risques.
Déclencheur sur la modification d'un attribut : trigger
[modifier | modifier le wikicode]Lors la modification d'un attribut on veut avoir une fonction qui soit appelée.
Soit le fichier Personnage.pm :
package Personnage; # on crée la classe « Personnage »
use Moose; # Utilisation de Moose pour créer une classe
has nom => ( 'is' => 'rw',
trigger => \&_renommer ); # renvoie une référence vers la fonction "_renommer" de l'espace de nom courant
sub _renommer()
{
my ($self, $nouveau, $ancien) = @_;
say "Ancien nom : " . $ancien if @_ > 2;
say "Nouveau nom : " . $nouveau ;
}
1; # Permet de s'assurer qu'une classe a bien été chargée
Soit le fichier Jeu.pl :
use Personnage; # pour utiliser notre module "Personnage"
my $perso = Personnage->new();
$perso->nom('Sam');
# Affichage de "Nouveau nom : Sam"
$perso->nom('Luc');
# Affichage de
# "Ancien nom : Sam"
# "Nouveau nom : Luc"
Références faibles : weak_ref
[modifier | modifier le wikicode]Tout d'abord, petit point sur la façon dont Perl gère la mémoire.
En Perl, nous ne manipulons pas directement la mémoire de l'ordinateur, la gestion est déléguée directement à Perl. Quand on n'utilise plus une variable, ou un objet, il faut « libérer » la zone mémoire qu'elle occupe. Pour trouver ce qu'il faut libérer, on utilise un ramasse-miettes (Garbage Collector). Il y a différentes manières d'implémenter un ramasse-miettes, celle utilisée par Perl est le comptage de références.
Le principe est assez simple : pour chaque variable on va compter le nombre de fois qu'on a une référence qui pointe dessus, une fois qu'on n'en a plus, on ne peut plus accéder à cette zone mémoire. Par conséquent, on peut libérer la mémoire puisque la variable n’est de toutes manières plus utilisable. Pour plus d'informations, je conseille d'aller voir la page sur les ramasse-miettes sur wikipedia.
Soit le module Personnage tel que :
package Personnage; # on crée la classe « Personnage »
use Moose; # Utilisation de Moose pour créer une classe
has nom => ( 'is' => 'rw' );
has [ 'papa', 'maman' ] => ( 'is' => 'rw', weak_ref => 1, isa => 'Personnage' );
1; # Permet de s'assurer qu'une classe a bien été chargée
Soit le code utilisant ce module :
use Personnage; # pour utiliser notre module "Personnage"
my $enfant = Personnage->new( nom => "Philippe");
my $papa = Personnage->new( nom => "Serge" );
$enfant->papa( $papa );
{
my $maman = Personnage->new( nom => "Bernadette" );
$enfant->maman( $maman );
}
# l'objet $maman n'existe plus, il n'y a plus de référence sur cette variable dans le code
say $enfant->papa->nom; # affichera "Serge"
say $enfant->maman->nom; # affichera une erreur d'exécution
Héritage : extends
[modifier | modifier le wikicode]Comme nous avons vu en introduction de ce chapitre, l'héritage permet de créer des classes décrivant toujours un objet plus précis. Par exemple, pour un jeu vidéo on fait une classe "Personnage" puis une classe "Magicien". Un magicien est un personnage. Il hérite donc de toutes ses propriétés et en rajoute potentiellement.
Soit le module Personnage tel que :
package Personnage; # on crée la classe « Personnage »
use Moose; # Utilisation de Moose pour créer une classe
has nom => ( 'is' => 'rw' );
1; # Permet de s'assurer qu'une classe a bien été chargée
Maintenant on veut créer le magicien qui, en plus d'avoir un nom, pourra lancer une boule de feu. Soit le module Magicien :
package Magicien;
use strict;
use Moose; # Utilisation de Moose pour créer une classe
use v5.14;
extends "Personnage"; # on hérite de la classe Personnage
sub lancer_boule_de_feu() {
my ($self) = @_;
say "Le magicien " .
$self->nom . # le magicien est un personnage, donc il possède un nom
" lance une boule de feu !";
}
Maintenant on essaie notre classe :
use Magicien;
my $magicien = Magicien->new( nom => "George" ); # soit un magicien nommé "George"
$magicien->lancer_boule_de_feu(); # affichera "Le magicien George lance une boule de feu !"
Perl permet l'héritage multiple, ce qui veut dire qu'une classe peut hériter de plusieurs autres classes, voici la syntaxe :
extends "classe1", "classe2";
Modifier une méthode héritée (surcharge)
[modifier | modifier le wikicode]Lors d'un héritage, on souhaite parfois changer une méthode dont on hérite, cela s'appelle une surcharge. Pour cela nous n'avons qu'à recréer une méthode de même nom qui remplacera l'ancienne.
Modifier un attribut hérité : +
[modifier | modifier le wikicode]On souhaite parfois changer une option de l'attribut, comme par exemple rendre un attribut non modifiable (ro) ou donner une valeur par défaut à un attribut, pour cela on peut modifier la définition de l'attribut. Cela se fait simplement en rajoutant un "+" devant le nom de l'attribut dans notre classe fille (celle qui hérite).
package Magicien;
use Moose; # Utilisation de Moose pour créer une classe
extends "Personnage"; # on hérite de la classe Personnage
has '+nom' => ( is => 'rw', default => 'Gandalf le Gris' ); # un magicien a un nom par défaut
Attention cependant, il est déconseillé de redéfinir un attribut juste pour changer sa valeur par défaut, on préfèrera donner une valeur par défaut à un attribut dans le constructeur de la classe.
Les rôles : Moose::Role with requires
[modifier | modifier le wikicode]Un rôle n'est pas à proprement parler une classe, mais plutôt une alternative à la hiérarchie imposée par la programmation orientée objet. Si nous reprenons des exemples déjà vus : un magicien est un personnage. Un magicien peut aussi être un guérisseur (qui soigne les autres personnages) mais ce n'est pas nécessairement le cas. On peut dire qu'il remplit la fonction de guérisseur, il a ce rôle.
Un rôle est défini comme une classe, avec éventuellement des attributs et des méthodes et utilise Moose::Role en lieu et place de Moose. Ce que nous allons faire généralement avec les rôles c'est de les utiliser pour indiquer des fonctions qui doivent apparaître dans la classe qui a ce rôle. Pour cela nous utilisons le mot clé "requires".
Soit le rôle Guerisseur :
package Guerisseur;
use Moose::Role; # Utilisation de Moose::Role pour définir un rôle
# toutes les classes ayant comme rôle "Guerisseur"
# devront avoir une méthode "soigner"
requires 'soigner';
1; # comme pour une classe
Notre classe Magicien :
package Magicien;
use Moose; # Utilisation de Moose pour créer une classe
extends "Personnage"; # on hérite de la classe Personnage
with "Guerisseur"; # on utilise le mot clé "with" pour indiquer qu'on possède un rôle
# la méthode qu'on _doit_ implémenter (rôle Guerisseur)
sub soigner() {
my ($self, $personnage_blesse) = @_;
say "Le magicien " . $self->nom . " soigne " . $personnage_blesse->nom ;
}
1;
Maintenant faisons un essai sur notre code :
use Magicien; # classe définie plus haut
use Guerrier; # classe ne faisant qu'hériter de Personnage
my $magicien = Magicien->new( nom => "George" );
my $guerrier = Guerrier->new( nom => "Jean-hubert" );
$magicien->soigner($guerrier); # affichera "Le magicien George soigne Jean-hubert"
Un rôle ne peut pas être instancié. Si on créer des méthodes ou qu'on défini des attributs dans le rôle, alors les classes qui ont ce rôle auront aussi ces attributs et ces méthodes, comme en cas d'héritage.
Ressources
Liens web
[modifier | modifier le wikicode]- Apache/CGI#Perl
- Documentation Perl en français
- Créer de la documentation avec POD
- PerlFR
- Guide Perl - Débuter et progresser en Perl (Sylvain Lhullier)
- Articles des mongueurs
- Sources DocBook
Livres autour de Perl
[modifier | modifier le wikicode]- Perl - Pocket Reference, de Johan Vromans, O'Reilly
- Perl Moderne, de Sébastien Aperghis-Tramoni, Philippe Bruhat, Damien Krotkine, Jérôme Quelin, Pearson
GFDL | Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans texte de dernière page de couverture. |
- ↑ https://unicode-table.com/fr/
- ↑ https://docstore.mik.ua/orelly/webprog/pcook/ch13_05.htm
- ↑ https://www.regular-expressions.info/posixbrackets.html
- ↑ https://www.regular-expressions.info/unicode.html
- ↑ https://www.regextester.com/15
- ↑ Jan Goyvaerts, Steven Levithan, Regular Expressions Cookbook, O'Reilly Media, Inc., (lire en ligne)
- ↑ Les options sont appelées modificateurs (modifiers en anglais), voir https://www.regular-expressions.info/modifiers.html