Skip to content

Linux Attitude

Le libre est un état d'esprit

Archive

Tag: Perl

Niveau : Star Star Star Empty Empty
Résumé : perlre

Suite à l'article précédent, qui posait les bases des expressions régulières, vous êtes probablement resté sur votre faim. Voici quelques astuces permettant d'élever le niveau et de faire des expressions mieux construites, plus rapides, plus courtes ...

Traitement des lignes

Fonctionnalités que tous les outils ne proposent pas, mais qui sont inclues dans le langage PCRE, /m et /s sont des modificateurs comme dans 's/toto/tata/m'.

Il faut bien distinguer les 2 qui portent assez facilement à confusion. De plus les 2 peuvent être utilisées en même temps.

  • /m est utilisé pour changer la syntaxe de '^' et '$' qui habituellement matchent le début et la fin de la chaîne, avec /m ils matcheront le début et la fin de chaque ligne.
  • /s est utilisé pour changer la syntaxe de '.' qui habituellement matche n'importe quel caractère sauf le changement de ligne, avec /s il matchera n'importe quel caractère y compris le changement de ligne

Ces modificateurs ne font la différence que lorsqu'on veut toucher aux caractères de retour à la ligne et plus généralement, lorsqu'on manipule des chaînes de caractère multilignes.

Gloutonnerie

L'algorithme de matching est glouton par défaut, ce qui veut dire que l'expression suivante :

/(.*):/

Marchera tous les éléments qui se trouvent avant le dernier ':' dans

root:x:0:0:root:/root:/bin/bash

On peut désactiver la gloutonnerie (greediness) en utilisant ? :

/(.*?):/

Qui du coup ne matchera plus que le premier élément.

Backreference

Il est possible de référencer un élément capturé à l'intérieur d'une expression régulière, ce qui permet de détecter les répétitions.

Exemple pour matcher le début et la fin d'une balise html.

/<(\w+)>.*?</\1>/

Extensions

Un certain nombre d'extensions d'expressions régulières ont été définies et commencent par '(?' on les utilise peu, mais elles sont très pratiques pour optimiser (en complexité comme en vitesse) des expressions régulières.

Match sans capture

(?:regex)

Permet de matcher sans capturer le contenu dans une variable. Pratique pour éviter la complexité liée à la numérotation des parenthèses imbriquées

Lookahead et lookbehind

Il est possible de matcher des éléments "s'ils sont suivis de" (ou "précédés", ou "ne sont pas"). Ceci permet d'écrire des expressions correspondant beaucoup mieux à la description

qu'on en fait, et parfois même d'écrire ce qui était impossible.
# matche "very funny" et "not boring" mais pas "not funny"
/(\w+ )((?<!not )funny|boring)/

Modificateur inline

Il es possible de changer un modificateur temporairement, par exemple pour rendre le matching insensible à la casse sur un seul mot :

# seul toto peut être écrit indifféremment avec des majuscules et des minuscules
/Bonjour (?i)toto(?-i) la moule/

Encore plus

Vous voulez encore plus de fonctionnalités ? Perl 5.10 apporte son lot de nouveautés dans les expressions régulières. Celles sont-ci sont aussi disponibles dans PCRE pour la plupart puisqu'elles en sont inspirées.

Je ne vais pas vous réécrire cet article très bien fait sur le sujet. Ces nouveautés seront surtout utiles pour les utilisateurs avancés.

Niveau : Star Empty Empty Empty Empty
Résumé : perl -pe 's///'

Il arrive régulièrement d'avoir à développer des expressions régulières. Si on excepte grep et sed, la plupart des outils utilisant des expressions régulières sont compatibles perl (en utilisant PCRE). C'est donc un bon point. Apprendre les expression régulières (qu'on peut considérer comme un langage à part entière) ne sera pas perdu même si on change d'outil ou de langage.

Tests

Avant de développer une expression régulière, je vous propose de vous familiariser avec le test de ces expressions. Si vous avec une ligne de commande c'est tout simple, hop un test ligne par ligne :

$ perl -pe 's/expression/########/'
données
a matcher, 
autant que vous voulez
<ctrl-d>

Mais si vous préférez un interface plus évoluée : voici un des nombreux qui permettent de tester vos expressions en direct http://myregexp.com/ et avec coloration des éléments matchés.

Syntaxe de base

Une expression régulière, c'est une chaîne de caractère qui a pour but de repérer des informations dans un texte. Elles sont en général implémentées sous forme d'automates à état finis. Cette méthode est extrêmement efficace et dans la plupart des cas il est très difficile de faire plus rapide.

Cette chaîne peut être utilisée pour chercher des informations dans un texte, mais aussi pour faire des remplacements automatisés. Perl utilise une syntaxe autour des expressions régulières pour préciser comment les utiliser. Celle-ci est souvent reprise par les utilisateurs des PCRE mais ca n'a rien d'obligatoire :

  • /expression/ : pour faire des recherches
  • s/expression/remplacement/ pour faire du recherche/remplacer
  • y/expression/remplacement/ pour faire des substitutions de caractères

y/// est un cas particulier que nous ignorerons.

Chacune de ces chaînes peut être suivie par un modificateur. Le plus utile est /i qui permet de ne pas se soucier de la casse. Exemple : /toto/i matchera ToTo. Le deuxième plus important est /g qui permet de faire le remplacement autant de fois que c'est possible et non pas seulement la première fois que l'expression matche.

Attention, hors perl, il se peut que tout ceci ne soit pas valable, mais cela ne change rien quant à la syntaxe des expressions expliquée ci après.

Syntaxe des expressions

Le principe d'une expression régulière (abrégé en regex) est de matcher des milliers de possibilités sans devoir toutes les écrire. Il ne s'agit donc (quasiment) que de "raccourcis".

Donc commençons par les non-raccourcis :

  • () : les parenthèses ont deux fonctions, grouper des éléments entre eux et mémoriser le contenu matché pour le rendre réutilisable par la suite
  • | : opérateur OU comme dans "confiture ou nutella ?"

Exemples :

^vive (la confiture|le nutella)$

Maintenant les raccourcis :

  • . : n'importe quel caractère
  • ^ $ : début et fin de chaîne
  • [abf] : a ou b ou f
  • [a-m] : a ou b ou ... ou m
  • [^a b] : tout sauf a ou espace ou b
  • \d : un chiffre
  • \w : un caractère alphanumérique
  • \W : un caractère non alphanumérique
  • \s : un caractère d'espacement
  • \S : un caractère de non-espacement
  • a? : 0 ou 1 fois a
  • b+ : 1 ou plus fois b
  • c* : 0 ou plus fois c
  • d{3} : 3 fois d
  • ...

Il y en a plein d'autres, mais avec celles-ci vous saurez vous débrouiller dans la plupart des situations.

Exemples :

# récupérer le hostname d'un nom de domaine dans $1
(\w+)(\.\w+)*
# récupérer une heure/minute/seconde en début de ligne dans $1, $2 et $3
^(\d\d):(\d\d):(\d\d)\s
# matche une ligne variable="valeur" avec ou sans guillemets et des espaces autorisés
^\s*(\w+)\s*=\s*"?(.*)"?

Après cette petite initiation sans conséquences, vous pouvez aller lire la vraie documentation fournie en manpage avec la documentation perl man perlre.

Niveau : Star Star Star Empty Empty
Résumé : perl

Quelques trucs et astuces de perlistes en vrac. Pour des vrais astuces de pro régulières, n'hésitez pas à visiter le site des moines de perl.

Initialiser un tableau de chaînes sans avec une syntaxe légère, très lisible quand on n'a que des mots, le séparateur étant les caractères blancs, incluant le retour à la ligne :

@tableau = qw( mot1 mot2 mot3 );

Passer du mode script de quelques lignes au mode développement long. Cela vous oblige à déclarer et initialiser toutes vos variables entre autre :

use strict;

Le debug facile :

use Data::Dumper;
# variable peut être n'importe quoi, toutes les sous-sous parties sont écrites :
print Dumper($variable,\@variable);
print Dumper(\%variable);

L'aide en ligne facile, ça fait tout de suite classe dans un script (format) :

use Pod::Usage;
# imprime le contenu de SYNOPSIS et termine le programme avec un code d'erreur 1
pod2usage(1);
# attention, pod est chatouilleux sur les retours à la ligne avant et après les =

=head1 SYNOPSIS

commande  [-o|--option=<valeur>]

=cut

La page de manuel facile (pas seulement le SYNOPSIS comme précédemment) :

# où fichier.pl est le code précédent
$ pod2man fichier.pl > fichier.1
$ man ./fichier.1

Et le dernier pour la route, le parsing d'options facile, très classe, surtout en combinaison avec pod2usage (format) :

use Getopt::Long;
my $options = {
        option => 'defaut'
};
GetOptions( $options, qw(
        option|o=s
));