350 likes | 447 Views
Perl : Notions avancées. Expressions rationnelles/régulières Utilisation en Perl Tableaux associatifs Fonctions prédéfinies. Perl : expressions régulières.
E N D
Perl : Notions avancées • Expressions rationnelles/régulières • Utilisation en Perl • Tableaux associatifs • Fonctions prédéfinies
Perl : expressions régulières Perl offre une grande puissance de manipulation d'expressions régulières. En Perl on utilise les expression régulières en tant que motifs de recherche.On utilise l'opérateur conditionnel =~ qui signifie « ressemble à » (matches). Syntaxe : $chaîne =~ /expression/ Exemple : if ($nom =~ /^[Dd]upon/) {print "OK !";} => Ok si nom est 'dupont', 'dupond', 'Dupont-Lassoeur' ^ signifie « commence par » On peut rajouter i derrière l'expression pour signifier qu'on ne différencie pas les majuscules des minuscules. Le contraire de l'opérateur =~ est !~ (ne ressemble pas à ...) if ($nom !~ /^dupon/i) {print "Non...";}
Perl : expressions régulières \s = espace ou tabulation ou retour-chariot « white space » \n = retour-chariot \t = tabulation ^ = début de chaîne $ = fin de chaîne a = a . = un caractère quelconque (sauf fin de ligne) Documentation complète: « perldoc perlre » ou http://www.perldoc.com/perl5.8.0/pod/perlre.html ou http://perl.asylog.net/DocFr/perlre.html
Perl : expressions régulières [a-z] tout caractère minuscule [aeiouy] toute voyelle [a-zA-Z0-9] tout caractère alphanumérique ^ au début d'un ensemble indique le complément de l'ensemble [^0-9] tout caractère non numérique \w un caractère alphanumérique ou _ (sans à, é, etc.) \W tout sauf un \w \d un chiffre (= [0-9]) \D tout sauf un \d (= [^0-9]) \S tout sauf un \s \b début ou fin d’un mot, soit entre \w et \W ou vice versa (utile surtout en anglais)
Perl : expressions régulières Quelques opérateurs : « ? » 0 ou 1 fois, a? = 0 ou 1 a « * » 0 fois ou plus, a* = 0 ou plusieurs a « + » 1 fois ou plus, a+ = 1 ou plusieurs a (ab)+= 1 ou plusieurs ab « | » : ou (inclusif) a|b = a ou b [^abc] tous caractères qui ne sont pas a ou b ou c {n,m} de n à m fois a{1,4} = 1 à 4 a {n,} n fois ou plus a{3,} = 3 a ou plus
Perl : expressions régulières Remarque : Si l'on veut qu'un méta-caractère apparaisse tel quel, il faut le précéder d'un « back-slash » (\). Les méta-caractères sont : . ^ | ( ) [ ] { } \ / $ + * ? ex : if (/\(.*\)/) {print;} affiche toute ligne contenant des parenthèses
Perl : expressions régulières – exemples Pour vérifier si une chaîne $x contient la chaîne "abc'' if ($x =~ /abc/) { . . . }Pour vérifier si une chaîne $x commence par la chaîne "abc''if ($x =~ /^abc/) { . . . }Pour vérifier si une chaîne $x commence par une majuscule if ($x =~ /^[A-Z]/) { . . . } Pour vérifier si une chaîne $x ne commence pas par une minuscule if ($x =~ /^[^a-z]/) { . . . } Ici le premier ^ signifie le début de la chaîne, tandis que le deuxième ^ à l'intérieur des crochets indique le complément de l'ensemble indiqué.
Perl : Remplacement Perl permet de faire des remplacements sur une chaîne des caractères, en utilisant la syntaxe : $chaîne =~ s/motif/remplacement/; où motif est une expression régulière et remplacement ce qui remplace. Exemples : $fruit =~ s/e$/es/; remplace un e final par es $tel =~ s/^99\./02.99\./; remplace des numéros de téléphone par une nouvelle numérotation
Perl : Remplacement On peux référencer une partie du motif dans le remplacement avec $1 ($1 est une variable correspondant au contenu de la première parenthèse). Exemple : Transformer automatiquement les noms d'arbres par « arbre à fruit » $texte =~ s/([a-z]+)ier /arbre à $1es /; 'cerisier' sera traduit par 'arbre à cerises' (contenu de $1 => 'ceris') 'manguier' => 'arbre à mangues' $2 correspond à la deuxième parenthèse, $3 à la troisième etc. Les options s/exp/remp/i; => Indifférenciation minuscules/majuscules s/exp/remp/g; => Remplace toutes les occurrences (pas seulement la première) Exemple : Pour remplacer un texte par le même en majuscule (\U) s/([a-z])/\U$1/g;
Perl : Remplacement Pour remplacer les suites de plusieurs a en un seul a : $x =~ s/a+/a/g; Pour enlever les suites de a : $x =~ s/a*//g; Ou tout simplement : $x =~ s/a//g; Pour enlever les espaces au début d'une chaîne : $x =~ s/^\s+//; Et à la fin de la chaîne : $x =~ s/\s+$//;
Perl : Remplacement – exemple complexe Supposons qu’on ait un texte en anglais américain étiqueté et qu’on veuille le convertir en anglais britannique. Plus précisément, on veut trouver les verbes en « -ize » et les réécrire « -ise », mais seulement ceux avec un radical d’au moins 5 lettres. Par exemple : USA : Analyzing the situation, he finalized his conclusions and sized the problem. R.U. : Analysing the situation, he finalised his conclusions and sized the problem. Le texte étant étiqueté, l’entrée du programme sera : Analyzing/VBG the/DT situation/NN ,/, he/PRP finalized/VBD his/PRP$ conclusions/NNS and/CC sized/VBD the/DT problem/NN ./.
Perl : Remplacement – exemple complexe Première solution : Solution avancée et plus facile à lire :
Perl : Remplacement – exemple complexe Première solution (corrigée): s/(^| )(\w{4,}[b-df-hj-np-tv-xz][iy])z(e|es|ed|ing)\/VB/$1$2s$3\/VB/g; Solution avancée et plus facile à lire : s/ \b # début du mot (\w{4,}) # $1 : 4 lettres ou plus ([b-df-hj-np-tv-xz][iy])# $2 : un consonne et i ou y z #le z (e|es|ed|ing) # $3 : terminaison conjuguée (?=\/VB) # assertion « vide » : étiquette d’un verbe /$1$2s$3/gx;
Les tableaux associatifs Les tableaux sont des structures qui consistent de paires de clés et valeurs. La clé est toujours un index numérique. Les index sont consécutifs. Les tableaux associatifs sont des structures qui consistent de paires de clés et valeurs où les clés ne sont pas nécessairement numériques ni consécutives (et donc sans ordre prédéfini). Ils sont toujours précédés du caractère % : my %prix = (’amande’, 30, ’fraise’, 9, ’cerise’, 25); ou my %prix = (’amande’ => 30, ’fraise’ => 9, ’cerise’ => 25); On fait ensuite référence à un élément du tableau par : $prix{’cerise’}
Les tableaux associatifs Exemples : my %chiffre = (); $chiffre{’un’} = 1; $chiffre{’deux’} = 2; print $chiffre{’deux’}; => 2 my$var = ’un’; print $chiffre{$var}; => 1 Remarque les tableaux associatifs utilisent les accolades, tandis que les tableaux utilisent les crochets. Donc, $x{2} retourne la valeur associée à la clé 2 dans le tableau associatif %x, tandis que $x[2] retourne le troisième élément du tableau @x. Attention : $x, @x et %x peuvent exister en même temps—ce sont trois variables distinctes!
Les tableaux associatifs Pour afficher toutes les clefs et les valeurs d'un tableau associatif, while ( my ($key,$val) = each %thearray ) { print $key, " ", $val, "\n"; } ou foreach my $key ( keys %prix ) { print $key, " ", $prix{$key}, "\n"; } Le code utilisant « each » est un peu plus rapide, mais celui utilisant « keys » permet de trier les clés avant de les imprimer : foreach my $key ( sort keys %prix ) { print $key, " ", $prix{$key}, "\n"; }
Exemple – fréquence des mots d'un texte #!/usr/bin/perl -w use strict; use warnings; # toujours utiliser ces deux instructions!!! $/ = ""; # unité de travail : le paragraphe my %wordcount; while (<>){ s/-\n//g; # enlever les tirets tr/A-Z/a-z/; # minusculiser my @words = split /\W*\s+\W*/, $_; foreach my $word (@words) { $wordcount{$word}++; } } foreach my $word (sort keys (%wordcount)) { printf "%20s,%d\n",$word,$wordcount{$word}; }
Exemple – fréquence des bigrammes d'un texte #!/usr/bin/perl -w use strict; use warnings; $/ = ""; my %wordcount; while (<>) { s/-\n//g; s/^\s+//;tr/A-Z/a-z/; my @words = split /\W*\s+\W*/, $_; for ( my $count = 0; $count <= $#words-1; $count++ ) { $wordcount{"$words[$count]$words[$count+1]"}++; } } foreach $wordpair (sort keys %wordcount) { printf "%20s,%d\n", $wordpair, $wordcount{$wordpair}; }
Exemple – fréquence des terminaisons (3 cars) my %ending;while(<>) { s/^\s+//; my @words = split(/\s+/,$_); foreach my $count (0 .. $#words) {# syntaxe plus pratique que for my @chars = split(//,$words[$count]); # on split sans séparateur # le résultat est un tableau de caractères if ($#chars > 1) { # on vérifie qu'il y ait au moins trois caractères $ending{$chars[$#chars-2] . " " . $chars[$#chars-1] . "" . $chars[$#chars]}++; } }} foreach my $end (sort keys %ending) { printf "%20s,%d\n", $end, $ending{$end}; }
Exemple – POS stripping Étant donné un texte étiqueté avec partie du discours (Parts-Of-Speech) The/det boy/noun ate/verb . . . enlever les partie du discours while(<>) { s/^\s+//;my @words = split; # paramètres implicites: (/\s+/,$_); for (my $count=0;$count<=$#words;++$count) {my $word = $words[$count]; # mais il faut enlever l'étiquette $word =~ s/\/.*$//;# si une chaîne commence avec un slash, et elle comporte # une suite de caractères, la remplacer avec la chaîne # vide.Ne pas oublier : il faut annuler l’interprétation # habituelle du méta-caractère / en le précédant de \. print $word, ""; } print "\n";}
Exemple – mots stripping Étant donné un texte étiqueté avec partie du discours (Parts-Of-Speech) The/det boy/noun ate/verb . . . enlever les mots et retourner les parties du discours while(<>) {s/^\s+//;my @words = split;foreach my $word (@words) {# syntaxe encore plus simple $word =~ s/^.*\///; # enlever le mot lui-même print $word, "";}print "\n";}
Exemple – le mot le plus long dans un texte my $maxlength = 0; my $longestword; while(<>) {my @words = split(/\s+/,$_);foreach my $word (@words) { @chars = split(//,$word); if ($#chars > $maxlength) { $maxlength = $#chars; $longestword = $word;}}} $maxlength++; #il faut ajouter 1, car l’indice commence à 0 print "$longestword $maxlength\n";
Exemple – unique #!/usr/bin/perl –w use strict; use warnings; # Eliminates all but the first occurrence of each line in a file # without otherwise changing the order. my %seen; while (<>) { if ($seen{$_}) { next; } else { $seen{$_}++; print; } }
Passage de paramètres au programme En Unix/Linux/Cygwin on peut appeler un programme Perl en lui donnant des paramètres, comme on le fait pour les commandes Unix. Les paramètres sont stockés dans un tableau spécial : @ARGV Le premier paramètre est donc $ARGV[0] $#ARGV est l'indice du dernier élément du tableau @ARGV, donc $ARGV[$#ARGV] est la valeur du dernier élément du tableau Exemple open(P, $ARGV[0]) || die "Couldn't open $ARGV[0]: $!";
Paramètres : options avancées Le module Perl Getopt::Long nous permet de spécifier facilement une série de paramètres aussi complexe qu’on le veut. Voire « perldoc Getopt::Long » ouhttp://www.perldoc.com/perl5.6.1/lib/Getopt /Long.html pour la documentation complète.Exemple : sub usage { print STDERR "Usage: $0 [-debug] [-count <n>] [-help] [-outfile <filename>] <infile>\n"; exit 1; } my $debug = 0; my $outfile = ""; my $help; my $count = 0; GetOptions(debug => \$debug, help => \$help, "outfile=s" => \$outfile,"count=i" => \$count, ) or usage; # Exit avec message en cas d’erreur # Maintenant, $debug, $outfile, $help et $count contiennent # les valeurs que l’usager a spécifiées pour les paramètres. $help and usage; # Exit avec message si l’usager tape -h my $infile = shift;# shift fournit le paramètre suivant
Fonctions prédéfinies – chaînes de caractères chop(ch)Enlève le dernier caractère de la chaîne ch. $ch=’cerises’; chop($ch);=> ch contient 'cerise‘ while(<>){chop; …} => le retour de chariot est enlevé de $_ chomp(ch) Même chose que « chop » mais enlève uniquement un retour de chariot en fin de chaîne. length(ch) Retourne le nombre de caractères de la chaîne ch. length(’cerise’)=> 6 uc(ch) Retourne la chaîne ch en majuscules. $ch = uc(’poire’)=> 'POIRE' $ch = uc(’PoIrE’)=> 'POIRE' lc(ch) Retourne la chaîne ch en minuscules. $ch = lc(’POIRE’)=> 'poire'
Fonctions prédéfinies – chaînes de caractères substr(ch, début, longueur) Retourne la chaîne de caractère contenue dans ch, commençant au caractère à l’indice début et de longueur longueur. $ch=substr(’dupond’, 0, 3) => 'dup' $ch=substr(’Les fruits’, 4) => 'fruits' index(ch, sousch, début) Retourne la position de la première instance de sousch dans ch. Si début est spécifié, il spécifie la position de départ pour la recherche. $i=index(’Le temps des cerises’, ’cerise’); => 13
Fonctions prédéfinies – tableaux, listes my @fruits = (’amande’, ’fraise’, ’cerise’); pop (tableau) Enlève et retourne le dernier élément de tableau. print pop(@fruits); => affiche 'cerise', @fruits devient ('amande','fraise') push (tableau, élément) Ajoute élément à la fin de tableau (contraire de pop). push(@fruits, ’abricot’); => @fruits devient ('amande','fraise','abricot') shift(tableau) Enlève et retourne le premier élément de tableau. print shift(@fruits) => Affiche 'amande', @fruits devient ('fraise','abricot') unshift (tableau, élément) Ajoute élément au début de tableau. unshift(@fruits, ’poire’); => @fruits devient ('poire','fraise','abricot')
Fonctions prédéfinies – tableaux, listes sort (tableau) Trie le tableau(en ordre ASCII croissant par défaut). @fruits = sort (@fruits); => @fruits devient ('abricot', 'fraise‘, ‘poire’) @fruits = sort {$b cmp $a} @fruits; # Tri inverse => @fruits devient (‘poire’, 'fraise‘, 'abricot') sort (10, 3, 5) => (10, 3, 5) # Tri ASCII sort {$a <=> $b} (10, 3, 5) => (3, 5, 10) # Tri numérique sort {$b<=> $a} (10, 3, 5) => (10, 5, 3) # Tri numérique inverse sort {$a cmp $b} (’e’, ’C’, ’a’) => (‘C’, ‘a’, ‘e’) # Tri ASCII sort (’e’, ’C’, ’a’) => (‘C’, ‘a’, ‘e’) # Tri ASCII sort {lc($a) cmp lc($b)} (’e’, ’C’, ’a’) => (‘a’, ‘C’, ‘e’) reverse (tableau) Inverse le tableau. @fruits = reverse(@fruits); => @fruits redevient ('abricot', 'fraise‘, ‘poire’)
Fonctions prédéfinies – tableaux, listes splice (tableau, début, nb, liste) Remplace nb éléments de tableau à partir de l'indice début avec les éléments dans liste. Retourne un tableau contenant les éléments enlevés. si @fruits = ('poire', 'fraise', 'abricot', 'cerise') et @legumes = ('carotte', 'tomate', 'céleri') @fruits2 = splice(@fruits, 1, 2, @legumes); => @fruits = ('poire', 'carotte', 'tomate', 'céleri', 'cerise') @fruits2 = ('fraise', 'abricot')
Fonctions prédéfinies – tableaux, listes splice (tableau, début, nb, liste) Dans les cas limites, on peut utiliser splice au lieu de shift, pop, unshift et push. $a = shift(@abz) $a = splice(@abz,0,1) $z = pop(@abz) $z = splice(@abz,$#abz,1) unshift (@abz,$a) splice(@abz,0,0,$a) push(@abz,$z) splice(@abz,$#abz+1,0,$z) substr (ch, début, longueur, remplacement) Si on spécifie un remplacement, comme avec splice, la sous chaîne est remplacée par remplacement. my $ch = "abcdef"; substr($ch, 2, 3, "12345"); => $ch = “ab12345f” substr($ch, 2, 3) = "12345"; # Code équivalent
Exemple – blankwords #!/usr/bin/perl –w use strict; use warnings; # blanks out given words # the words to remove should be in a file, one per line # check for correct usage if ($#ARGV < 0) { # pas de params print STDERR "usage: blankwords <word list> [<base file(s)>]\n"; exit 1; } open(P, $ARGV[0]) or die "Couldn't open killword file $ARGV[0]: $!"; my $killwords = "\n"; while (<P>) { $killwords .= $_; # $var OP= $val equivaut à $var = $var OP $val } close(P); shift; # s'applique à @ARGV
Exemple – blankwords while (<>) { my @token = split; # split on whitespace my @char = split("", $_); # split on nullspace to get char array my $offset = 0; for ( my $i = 0; $i < @token; $i++ ) { $offset = index($_, $token[$i], $offset); my $size = length($token[$i]); my $pat = "\Q$token[$i]\E"; # \Q quotes meta chars until \E if ($killwords =~ /\n$pat\n/) { foreach my $ind ($offset .. $offset + $size - 1) { splice(@char, $ind, 1, " "); # avec splice } # avec substr: substr($_, $offset, $size, " " x $size); } $offset += $size; } print @char; # avec splice # avec substr: print $_; }
Exemple – blankclasses #!/usr/bin/perl –w use strict; use warnings; # Blanks out words of a given POS in a tagged text the POS to remove # should be in a file, one per line. Words and tags are separated by :: # check for correct usage if ($#ARGV < 0) { print STDERR "usage: blankclasses <POS list> [<base file>]\n"; exit 1; } open(P, $ARGV[0]) or die "Couldn't open POS-list file $ARGV[0]: $!"; my $killclasses = "\n"; while (<P>) { $killclasses .= $_; } close(P); shift;
Exemple – blankclasses while (<>) { s/^ *//; # enlever les espace au début de ligne my @token = split; # split on whitespace for (my $i = 0; $i < @token; $i++) { # @token: scalar context if ($token[$i] =~ /(.+)::(.+)/) { # word::tag and back-ref my $word = $1; my $tag = $2; if ($killclasses =~ /\n$tag\n/) { $token[$i] = " " x length($token[$i]); } } else { die "Word or Tag missing from token $token[$i]\n"; } } print join(' ', @token), "\n"; }