NOM

perlfaq6 - Expressions Régulières (Revision: 1.22, Date: 1998/07/16 14:01:07)


DESCRIPTION

Cette section est incroyablement courte car les autres sont parsemées de réponses concernant des expressions régulières. Par exemple, décoder une URL et vérifier si quelque chose est un nombre ou non est du domaine des expressions régulières, mais ces réponses existent ailleurs dans la FAQ (dans le chapitre sur les Données et le Réseau pour l'exemple donné).


Comment utiliser les expressions régulières sans créer du code illisible et difficile à maintenir?

Trois méthodes peuvent rendre les expressions régulières faciles à maintenir et compréhensibles.

Commentaires en dehors de l'expression régulière

Décrivez ce que vous faites et comment vous le faites en utilisant la façon de commenter habituelle de Perl.

   # transforme la ligne en le premier mot, le signe deux points
   # et le nombre de caractères du reste de la ligne
   s/^(\w+)(.*)/ lc($1) . ":" . length($2) /meg;
Commentaires dans l'expression régulière

En utilisant le modificateur /x, les espaces seront ignorés dans le motif de l'expression régulière (excepté dans un regroupement de caractères), et vous pourrez aussi utiliser les commentaires habituels dedans. Comme vous pouvez l'imaginer, espaces et commentaires aident pas mal.

/x vous permet de transformer ceci:

   s{<(?:[^>'"]*|".*?"|'.*?')+>}{}gs;

en:

   s{ <                    # signe inférieur
       (?:                 # parenthèse ouvrante ne faisant pas de référence
            [^>'"] *       # 0 ou plus de caract. qui ne sont ni >, ni ' ni "
               |           #    ou 
            ".*?"          # une partie entre guillemets (reconnaissance min)
               |           #    ou
            '.*?'          # une partie entre apostrophes (reconnaissance min)
       ) +                 #   tout cela se produisant une ou plusieurs fois
      >                    # signe supérieur
   }{}gsx;                 # remplacé avec rien, cad supprimé

Ce n'est pas encore aussi clair que de la prose, mais c'est très utile pour décrire le sens de chaque partie du motif.

Différents Délimiteurs

Bien que nous pensons habituellement que les motifs sont délimités par le caractère /, ils peuvent être délimités par presque n'importe quel caractère. la page de manuel perlre l'explique. Par exemple, s/// ci dessus utilise des accolades comme délimiteurs. Sélectionner un autre délimiteur permet d'éviter de le quoter à l'intérieur du motif:

   s/\/usr\/local/\/usr\/share/g;        # mauvais choix de délimiteur
   s#/usr/local#/usr/share#g;            # meilleur


J'ai des problèmes pour faire une reconnaissance sur plusieurs lignes. Qu'est ce qui ne va pas?

Ou vous n'avez pas plus d'une ligne dans la chaine où vous faites la recherche (cas le plus probable), ou vous n'appliquez pas le bon modificateur à votre motif (cas possible).

Il y a plusieurs manières d'avoir plusieurs lignes dans une chaine. Si vous voulez l'avoir automatiquement en lisant l'entrée, vous initialiserez $/ (probablement à ``'' pour des paragraphes ou undef pour le fichier entier) ce qui vous permettra de lire plus d'une ligne à la fois.

Lire la page de manuel perlre vous aidera à décider lequel de /s et /m (ou les deux) vous aimeriez utiliser: /s permet au point de reconnaitre le caractère fin de ligne, et /m permet au circonflexe et dollar de reconnaître tous les débuts et fins de ligne, pas seulement le début et la fin de la chaîne. Vous pouvez l'utiliser pour être sûr que vous avez bien plusieurs lignes dans votre chaine.

Par exemple, ce programme détecte les mots répétés, même s'ils sont coupés (mais pas dans plusieurs paragraphes). Pour cet exemple, nous n'avons pas besoin de /s car nous n'utilisons pas le point dans l'expression régulière dont on veut qu'elle enjambe les lignes. Nous n'avons pas besoin non plus de /m car nous ne voulons pas que le circonflexe ou le dollar fasse une reconnaissance d'un début ou d'une fin d'une nouvelle ligne. Mais il est impératif que $/ soit mis pour autre chose que l'option par défaut, ou autrement nous n'aurons pas plusieurs lignes d'un coup à se mettre sous la dent.

   $/ = '';             # lis au moins un paragraphe entier, 
                        # pas qu'une seule ligne
   while ( <> ) {
       while ( /\b([\w'-]+)(\s+\1)+\b/gi ) { # les mots commencent par
                                             # des caractères alphanumériques
          print "$1 est répété dans le paragraphe $.\n";
       }
   }

Voilà le code qui trouve les phrases commençant avec ``From '' (qui devrait être transformés par la plupart des logiciels de courrier électronique):

   $/ = '';     # lis au moins un paragraphe entier, pas qu'une seule ligne
   while ( <> ) {
       while ( /^From /gm ) { # /m fait que ^ reconnaisse 
                              # tous les débuts de ligne
           print "from de départ dans le paragraphe $.\n";
       }
   }

Voilà le code qui trouve tout ce qu'il y a entre START et END dans un paragraphe:

   undef $/; # lis le fichier entier, pas seulement qu'une ligne
             # ou un paragraphe
   while ( <> ) {
       while ( /START(.*?)END/sm ) { # /s fait que . enjambe les lignes
           print "$1\n";
       }
   }


Comment extraire des lignes entre deux motifs qui sont chacun sur des lignes différentes?

Vous pouvez utiliser l'opérateur quelque peu exotique .. de Perl (documenté dans la page de manuel perlop):

   perl -ne 'print if /START/ .. /END/' fichier1 fichier2 ...

Si vous voulez du texte et non des lignes, vous pouvez utiliser

   perl -0777 -pe 'print "$1\n" while /START(.*?)END/gs' fichier1 fichier2 ...

Mais si vous voulez des occurrences imbriquées de START à l'intérieur de END, vous tombez sur le problème décrit, dans cette section, sur la reconnaissance de texte bien équilibré.

Ici un autre exemple d'utilisation de ..:

   while (<>) {
       $in_header =   1  .. /^$/;
       $in_body   = /^$/ .. eof();
       # maintenant choisissez entre eux
   } continue {
       reset if eof();                # fixe $.
   } 


J'ai mis une expression régulière dans $/ mais cela ne marche pas. Qu'est-ce qui est faux?

$/ doit être une chaine, et non une expression régulière. Awk est au moins meilleur pour quelque chose. :-)

En fait, vous pouvez faire ceci si vous n'avez pas à vous soucier de mettre le fichier entier en mémoire:

   undef $/;
   @records = split /your_pattern/, <FH>;

Le module Net::Telnet (disponible chez CPAN) a la capacité d'attendre pour un motif dans le flux d'entrée, ou fait un timeout si ce motif n'apparait pas dans un temps donné.

   ## Crée un fichier de trois lignes.
   open FH, ">file";
   print FH "La première ligne\nLa deuxième ligne\nLa troisième ligne\n";
   close FH;

   ## Lui met un handle de fichier en lecture/écriture.
   $fh = new FileHandle "+<file";

   ## L'attache à un objet "stream".
   use Net::Telnet;
   $file = new Net::Telnet (-fhopen => $fh);

   ## Cherche la deuxième ligne et imprime la troisième.
   $file->waitfor('/deuxième ligne\n/');
   print $file->getline;


Comment substituer indépendamment de la casse de la partie gauche de la substitution, mais en préservant la casse de la partie droite?

Cela dépend de votre sens de ``préserver la casse''. Le script suivant qui fait le remplacement met la même casse, lettre après lettre, que l'original. Si la chaîne de remplacement a plus de caractères que la chaine qui est remplacée, la casse du dernier caractère de la deuxième est utilisée pour le remplacement du surplus.

   # L'original est de Nathan Torkington, mis en forme par Jeffrey Friedl
   #
   sub preserve_case($$)
   {
       my ($old, $new) = @_;
       my ($state) = 0; # 0 = no change; 1 = lc; 2 = uc
       my ($i, $oldlen, $newlen, $c) = (0, length($old), length($new));
       my ($len) = $oldlen < $newlen ? $oldlen : $newlen;

       for ($i = 0; $i < $len; $i++) {
           if ($c = substr($old, $i, 1), $c =~ /[\W\d_]/) {
               $state = 0;
           } elsif (lc $c eq $c) {
               substr($new, $i, 1) = lc(substr($new, $i, 1));
               $state = 1;
           } else {
               substr($new, $i, 1) = uc(substr($new, $i, 1));
               $state = 2;
           }
       }
       # on se retrouve avec ce qui reste de new 
       # (quand new est plus grand que old)
       if ($newlen > $oldlen) {
           if ($state == 1) {
               substr($new, $oldlen) = lc(substr($new, $oldlen));
           } elsif ($state == 2) {
               substr($new, $oldlen) = uc(substr($new, $oldlen));
           }
       }
       return $new;
   }

   $a = "ceci est un TEsT de casse";
   $a =~ s/(test)/preserve_case($1, "succes")/gie;
   print "$a\n";

Cela affiche:

   Ceci est un SUcCES de casse


Comment faire pour que \w reconnaisse les caractères nationaux?

Regardez perllocale.


Comment reconnaître une version locale-intelligente de /[a-zA-Z]/?

Un caractère alphabétique doit être /[^\W\d_]/, quelle que soit la locale que vous utilisez. Les non-alphabétiques doivent être /[\W\d_]/ (sous réserve que vous ne considérez pas un souligné comme une lettre).


Comment protéger une variable pour l'utiliser dans une expression régulière?

L'analyseur syntaxique de Perl remplacera par leur valeur les occurences de $variable et @variable dans les expressions régulières à moins que le délimiteur soit une apostrophe. Rappelez vous aussi que la partie droite d'une s/// substitution est considérée comme une chaine entre guillemets (voir la page de manuel perlop pour plus de détails). Rappelez vous encore que n'importe quel caractère spécial d'expression régulière agira à moins que vous ne précédiez la substitution avec \Q. Voici un exemple:

   $string = "to die?";
   $lhs = "die?";
   $rhs = "sleep no more";

   $string =~ s/\Q$lhs/$rhs/;
   # $string est maintenant "to sleep no more"

Sans le \Q, l'expression régulière aurait faussement aussi reconnu ``di''.


A quoi sert vraiment /o?

Utiliser une variable dans une opération de reconnaissance avec expression régulière force une réévaluation (et peut-être une recompilation) à chaque fois qu'elle est appliquée. Le modificateur /o verrouille l'expression régulière la première fois qu'elle est utilisée. C'est toujours ce qui se passe avec une expression régulière constante, et en fait, le motif est compilé dans le format interne en même temps que le programme entier l'est.

Utiliser /o est peu pertinent à moins que le remplacement de variable soit utilisé dans le motif, et si cela est, le moteur d'expression régulière ne prendra pas en compte les modifications de la variable ultérieures à la toute première évaluation.

/o est souvent utilisé pour gagner en efficacité en ne faisant pas les évaluations nécessaires quand vous savez que cela ne pose pas de problème (car vous savez que les variables ne changeront pas), ou plus rarement, quand vous ne voulez pas que l'expression régulière remarque qu'elles changent.

Par exemple, voici un programme ``paragrep'':

   $/ = '';  # mode paragraphe
   $pat = shift;
   while (<>) {
       print if /$pat/o;
   }


Comment utiliser une expression régulière pour enlever les commentaires de type C d'un fichier?

Bien que cela puisse se faire, c'est plus compliqué que vous ne pensez. Par exemple, cette simple ligne

   perl -0777 -pe 's{/\*.*?\*/}{}gs' foo.c

marchera dans beaucoup de cas mais pas tous. Vous voyez, c'est un peu simplet pour certains types de programmes C, en particulier, ceux où des chaines protégées sont des commentaires. Pour cela, vous avez besoin de quelque chose de ce genre, créé par Jeffrey Friedl:

   $/ = undef;
   $_ = <>;
   s#/\*[^*]*\*+([^/*][^*]*\*+)*/|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|\n+|.[^/"'\\]*)#$2#g;
   print;

Cela pourrait, évidemment, être écrit plus lisiblement avec le modificateur /x en ajoutant des espaces et des commentaires.


Est ce possible d'utiliser les expressions régulières de Perl pour reconnaître du texte bien équilibré?

Quoique les expressions régulières de Perl soient plus puissantes que les expressions régulières ``mathématiques'', car elles présentent des avantages comme les motifs mémorisés (\1 et ses potes), elles ne sont pas encore assez puissantes. Vous avez encore besoin d'utiliser des techniques autres pour analyser du texte bien équilibré, tel que du texte enclos par des parenthèses ou des accolades.

Une sous routine élaborée (pour du 7-bit ASCII seulement) pour extraire de simples caractères se correspondant et peut-être imbriqués, comme ` et ', { et }, ou ( et ) peut être trouvée à http://www.perl.com/CPAN/authors/id/TOMC/scripts/pull_quotes.gz .

Le module C::Scan module du CPAN contient de telles sous routines pour des usages internes, mais elles ne sont pas documentées.


Que veut dire "les expressions régulières sont gourmandes"? Comment puis-je tourner autour?

La plupart des gens pensent que les expressions régulières gourmandes reconnaissent autant qu'elles peuvent. Techniquement parlant, c'est en réalité les quantificateurs (?, *, +, {}) qui sont gourmands plutôt que le motif entier; Perl préfère les gourmandises locales et les gratifications immédiates à une gloutonnerie globale. Pour avoir les versions non gourmandes des mêmes quantificateurs, utilisez (??, *?, +?, {}?).

Un exemple:

       $s1 = $s2 = "J'ai très très froid";
       $s1 =~ s/tr.*s //;      # J'ai froid
       $s2 =~ s/tr.*?s //;     # J'ai très froid

Notez que la seconde substitution arrête la reconnaissance dès qu'un ``s '' est rencontré. Le quantificateur *? dit effectivement au moteur des expressions régulières de trouver une reconnaissance aussi vite que possible et de passer le contrôle à la suite, comme de se refiler une patate chaude.


Comment j'examine chaque mot dans chaque ligne?

Utilisez la fonction split:

   while (<>) {
       foreach $word ( split ) { 
           # faire quelque chose avec $word ici
       } 
   }

Notez que ce ne sont pas vraiment des mots dans le sens français; ce sont juste des suites de caractères différents de l'espace.

Pour travailler avec seulement des séquences alphanumériques, vous pourriez envisager

   while (<>) {
       foreach $word (m/(\w+)/g) {
           # faire quelque chose avec $word ici
       }
   }


Comment afficher un rapport sur les fréquences de mots ou de lignes?

Pour faire cela, vous avez à sortir chaque mot du flux d'entrée. Nous appelerons mot une suite de caractères alphabétiques, de tirets et d'apostrophes plutôt qu'une suite de tout caractère sauf espace comme vue dans la question précédente:

   while (<>) {
       while ( /(\b[^\W_\d][\w'-]+\b)/g ) {   # on rate "`mouton'"
           $seen{$1}++;
       }
   }
   while ( ($word, $count) = each %seen ) {
       print "$count $word\n";
   }

Si vous voulez faire la même chose avec les lignes, vous n'avez pas besoin d'une expression régulière:

   while (<>) { 
       $seen{$_}++;
   }
   while ( ($line, $count) = each %seen ) {
       print "$count $line";
   }

Si vous voulez que le résultat soit trié, regardez la section sur Hashes.


Comment faire une reconnaissance approximative?

Regardez le module String::Approx du CPAN.


Comment reconnaître efficacement plusieurs expressions régulières en même temps?

Le code suivant est super-inefficace:

   while (<FH>) {
       foreach $pat (@patterns) {
           if ( /$pat/ ) {
               # faire quelque chose
           }
       }
   }

A la place, vous avez besoin d'utiliser l'un des modules expérimentals d'extension d'expression régulière du CPAN (qui devra être plus que transformée pour remplir vos besoins), ou mettre quelque chose de ce genre, inspiré d'une routine du livre de Jeffrey Friedl:

   sub _bm_build {
       my $condition = shift;
       my @regexp = @_;  # cela ne DOIT pas être local(); besoin de my()
       my $expr = join $condition => map { "m/\$regexp[$_]/o" } (0..$#regexp);
       my $match_func = eval "sub { $expr }";
       die if $@;  # propage $@; cela ne devrait pas arriver!
       return $match_func;
   }

   sub bm_and { _bm_build('&&', @_) }
   sub bm_or  { _bm_build('||', @_) }

   $f1 = bm_and qw{
           xterm
           (?i)window
   };

   $f2 = bm_or qw{
           \b[Ff]ree\b
           \bBSD\B
           (?i)sys(tem)?\s*[V5]\b
   };

   # remplis pour moi /etc/termcap, probablement
   while ( <> ) {
       print "1: $_" if &$f1;
       print "2: $_" if &$f2;
   }


Pourquoi les recherches de limite de mot avec \b ne marchent pas pour moi?

Deux idées fausses communes sont que \b est synonyme de \s+, et que c'est la frontière entre les espaces et les autres caractères. Aucune n'est correcte. \b est l'endroit entre un caractère \w et un \W (c'est cela, \b est la frontière d'un ``mot''). C'est une assertion de longueur nulle comme ^, $, et tous les autres anchors, d'où il ne consomme aucun caractère. la page de manuel perlre décrit le comportement de tous ces metacaractères.

Voici des exemples d'utilisation incorrecte de \b, avec les corrections:

   "deux mots" =~ /(\w+)\b(\w+)/;            # MAUVAIS
   "deux mots" =~ /(\w+)\s+(\w+)/;            # bon

   " =matchless= text" =~ /\b=(\w+)=\b/;   # MAUVAIS
   " =matchless= text" =~ /=(\w+)=/;       # bon

Quoiqu'ils peuvent ne pas faire ce que vous pensez qu'ils font, \b et \B peuvent être bien utiles. Pour un exemple d'utilisation correcte de \b, regardez l'exemple de reconnaissance de mots dupliqués sur plusieurs lignes.

Un exemple d'utilisation de \B est le motif \Best\B. Il trouvera les occurences de ``est'' seulement à l'intérieur des mots, comme ``geste'', mais pas ``test'' ou ``estime''.


Pourquoi l'utilisation de $&, $`, or $' ralentit tant mon programme?

Parce qu'une fois que Perl voit que vous avez besoin d'une de ces variables quelque part, il doit les fournir pour chaque reconnaissance de motif. Le même mécanisme qui les prend en compte est fourni pour l'utilisation de $1, $2, etc... donc vous payez le même prix pour chaque expression régulière qui contient des parenthèses de capture. Mais si vous n'utilisez jamais $&, etc... dans votre script, alors les expressions régulières sans capture de parenthèses ne sont pas pénalisées. Donc, évitez $&, $' et $` si vous pouvez, mais si vous ne pouvez pas (et c'est très apprécié pour plusieurs algorithmes), une fois que vous les avez uilisées, utilisez-les comme vous voulez, car vous avez déjà payé le prix.


A quoi est bon \G dans une expression régulière ?

La notation \G est utilisée dans une reconnaisssance ou substitution en conjonction avec le modificateur /g (et ignoré s'il n'y a pas de /g) pour ancrer l'expression régulière à l'endroit où la dernière reconnaissance a eu lieu, cad l'endroit indiqué par pos().

Par exemple, supposez que vous ayez une ligne de texte quotée dans un courrier standard avec la notation Usenet (cad, commençant par des >), et que vous vouliez changer chaque caractère de début > en :. Vous pouvez faire ainsi:

    s/^(>+)/':' x length($1)/gem;

Ou, en utilisant \G, le plus simple (et rapide):

   s/\G>/:/g;

Une utilisation plus sophistiquée peut entraîner un analyseur lexicographique. L'exemple suivant, à la lex, est de Jeffrey Friedl. Il ne marche pas avec la 5.003 à cause d'un bug dans cette version, mais est ok à partir de la 5.004. (Notez l'utilisation de /c, qui, lorsqu'une reconnaissance avec /g échoue, empêche de remettre la position de recherche au début de la chaîne).

   while (<>) {
     chomp;
     PARSER: {
          m/ \G( \d+\b    )/gcx    && do { print "nombre: $1\n";  redo; };
          m/ \G( \w+      )/gcx    && do { print "mot:   $1\n";  redo; };
          m/ \G( \s+      )/gcx    && do { print "espace:  $1\n";  redo; };
          m/ \G( [^\w\d]+ )/gcx    && do { print "autre:  $1\n";  redo; };
     }
   }

Bien sûr, cela pourrait avoir été écrit ainsi

   while (<>) {
     chomp;
     PARSER: {
          if ( /\G( \d+\b    )/gcx  {
               print "nombre: $1\n";
               redo PARSER;
          }
          if ( /\G( \w+      )/gcx  {
               print "mot: $1\n";
               redo PARSER;
          }
          if ( /\G( \s+      )/gcx  {
               print "espace: $1\n";
               redo PARSER;
          }
          if ( /\G( [^\w\d]+ )/gcx  {
               print "autre: $1\n";
               redo PARSER;
          }
     }
   }

Mais vous perdez l'alignement vertical des expressions régulières.


Les expressions régulières de Perl sont-elles AFD ou AFN? Sont-elles conformes à POSIX?

Bien qu'il soit vrai que les expressions régulières de Perl ressemblent aux AFD (automate fini déterminé) du programme egrep(1), elles sont dans les faits implémentées comme AFN (automate fini indéterminé) pour permettre le retour en arrière et la mémorisation de sous-motif. Et elles ne sont pas non plus conformes à POSIX, car celui-ci garantit le comportement du pire dans tous les cas. (Il semble que certains préfèrent une garantie de consistence, même quand ce qui est garanti est la lenteur). Lisez le livre ``Mastering Regular Expressions'' (from O'Reilly) par Jeffrey Friedl pour tous les détails que vous pouvez espérer connaître sur ces matières (la référence complète est dans la page de manuel perlfaq2).


Qu'est ce qui ne va pas avec l'utilisation de grep ou map dans un contexte vide?

Grep et map construisent tous les deux une liste qu'ils retournent, sans tenir compte du contexte. Cela signifie que vous mettez Perl dans le trouble de construire une liste que vous allez ensuite ignorer. Ce n'est pas une façon de traiter un langage de programmation, espèce de petit bandit !


Comment reconnaître des chaînes avec des caractères de plusieurs octets?

C'est dur et il n'y a pas de bonne manière. Perl ne supporte pas directement les grands caractères. Il prétend qu'un octet et un caractère sont synonymes. L'ensemble d'approches suivantes est offert par Jeffrey Friedl, dont l'article dans le numéro 5 du Perl Journal parle de cela de manière détaillée.

Supposons que vous ayiez un codage de ces mystérieux Martiens où les paires de lettre majuscules ASCII codent une lettre simple martienne (i.e. les 2 octets ``CV'' donne une simple lettre martienne, ainsi que ``SG'', ``VS'', ``XX'', etc.). D'autres octets représentent de simples caractères comme l'ASCII.

Ainsi, la chaîne martienne ``Je suis CVSGXX!'' utilise 15 octets pour coder les 12 caractères 'J', 'e', ' ', 's', 'u', 'i', 's', ' ', 'CV', 'SG', 'XX', '!'.

Maintenant, vous voulez chercher le simple caractère /GX/. Perl ne connaît rien au martien, donc il trouvera les 2 octets ``GX'' dans la chaîne ``Je suis CVSGXX!'', alors que ce carctère n'y est pas: il semble y être car ``SG'' est à côté de ``XX'', mais il n'y a pas de réel ``GX''. C'est un grand problème.

Voici quelques manières, toutes pénibles, de traiter cela:

   $martian =~ s/([A-Z][A-Z])/ $1 /g; # assurer que les octets ``martiens''
                                      # ne soient plus contigüs
   print "GX trouvé!\n" if $martian =~ /GX/;

Ou ainsi:

   @chars = $martian =~ m/([A-Z][A-Z]|[^A-Z])/g;
   # c'est conceptuellemnt similaire à:     @chars = $text =~ m/(.)/g;
   #
   foreach $char (@chars) {
       print "GX trouvé!\n", last if $char eq 'GX';
   }

Ou encore ainsi:

   while ($martian =~ m/\G([A-Z][A-Z]|.)/gs) {  # \G probablement inutile
       print "GX trouvé!\n", last if $1 eq 'GX';
   }

Ou encore ainsi:

   die "désolé, Perl ne supporte pas (encore) le Martien )-:\n";

En supplément, un simple programme qui convertit des demi-largeurs en pleine largeur katakana (dans le codage Shift-JIS ou EUC) est disponible au CPAN comme

Il y a plusieurs double- (et multi-) octets codages courament utilisés en ce moment. Plusieurs versions de ceux-ci ont des caractères de 1-, 2-, 3- et 4-octets, tous mélangés.


AUTEUR ET COPYRIGHT

Copyright (c) 1997, 1998 Tom Christiansen et Nathan Torkington. Tous droits réservés.

Lorsque ce travail est inclus comme un élément de la distribution standard de Perl, ou comme une partie de sa documentation complète sous forme imprimée ou autrement, il ne peut être distribué que dans les limites fixées par la Perl's Artistic License. Toute distribution de ce fichier ou de ses dérivés hors de cet ensemble nécessite un accord particulier avec le titulaire des droits.

Indépendemment de sa distribution, tous les exemples de code de ce fichier sont ici placés dans le domaine public. Vous êtes autorisés et encouragés à utiliser ce code dans vos programmes que ce soit pour votre plaisir ou pour un profit. Un simple commentaire dans le code précisant l'origine serait de bonne courtoisie mais n'est pas indispensable.


TRADUCTION

Marianne Roger (maroger@maretmanu.org) La traduction française est distribuée avec les même droits que sa version originale (voir ci-dessus).


RELECTEUR

Régis Julié (Regis.Julie@cetelem.fr)