Content-type: text/html Manpage of PERLFAQ6

PERLFAQ6

Section: User Contributed Perl Documentation (1)
Updated: 2003-03-13
Index Return to Main Contents
 

NOM

perlfaq6 - Expressions Rationnelles ($Revision: 1.27 $, $Date: 1999/05/23 16:08:30 $)  

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 rationnelle
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 rationnelle
En utilisant le modificateur "/x", les espaces seront ignorés dans le motif de l'expression rationnelle (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é par 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 pensions 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. 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 chaîne 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 chaîne. 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 perlre vous aidera à décider lequel de "/s" et "/m" (ou les deux) vous aimeriez utiliser : "/s" permet au point de reconnaître 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 perlop):

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

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

   perl -0777 -ne '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 rationnelle dans $/ mais cela ne marche pas. Qu'est-ce qui est faux ?

$/ doit être une chaine, et non une expression rationnelle. 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 ?

Voici une adorable solution perlienne par Larry Rosler. Elle exploite les propriétés du xor bit à bit sur les chaînes ASCII.

    $_= "this is a TEsT case";

    $old = 'test';
    $new = 'success';

    s{(\Q$old\E}
     { uc $new | (uc $1 ^ $1) .
        (uc(substr $1, -1) ^ substr $1, -1) x
            (length($new) - length $1)
     }egi;

    print;

Et la voici sous la forme d'un sous-programme, selon le modèle ci-dessus :

    sub preserve_case($$) {
        my ($old, $new) = @_;
        my $mask = uc $old ^ $old;

        uc $new | $mask .
            substr($mask, -1) x (length($new) - length($old))        
    }

    $a = "this is a TEsT case";
    $a =~ s/(test)/preserve_case($1, "success")/egi;
    print "$a\n";

Ceci affiche :

    this is a SUcCESS case

Rien que pour montrer que les programmeurs C peuvent écrire du C dans tous les langages de programmation, si vous préférez une solution plus proche du style de C, le script suivant fait en sorte que la substitution a la même casse, lettre par lettre, que l'original (il s'avère aussi tourner environ 240 % plus lentement que la version perlienne). Si la substitution a plus de caractères que la chaîne substituée, la casse du dernier caractère est utilisée pour le reste de la substitution.

   # 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 < $newlenE<nbsp>? $oldlenE<nbsp>: $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;
   }

 

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 rationnelle ?

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 ne soit une apostrophe. Rappelez-vous aussi que la partie droite d'une "s///" substitution est considérée comme une chaîne entre guillemets (voir perlop pour plus de détails). Rappelez-vous encore que n'importe quel caractère spécial d'expression rationnelle 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 rationnelle aurait faussement aussi reconnu ``di''.  

À 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 rationnelle 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 ne 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 rationnelle remarque qu'elles changent.

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

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

 

Comment utiliser une expression rationnelle 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, modifié ultérieurement par Fred Curtis :

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

Cela pourrait, évidemment, être écrit plus lisiblement avec le modificateur "/x" en ajoutant des espaces et des commentaires. Le voici étendu, une courtoisie de Fred Curtis.

    s{
       /\*         ##  Début d'un commentaire /* ... */
       [^*]*\*+    ##  Non-* suivie par 1 ou plusieurs *
       (
         [^/*][^*]*\*+
       )*          ##  0 ou plusieurs choses ne commençant pas par /
                   ##  mais finissent par '*'
       /           ##  Fin d'un commentaire /* ... */

     |         ##     OU  diverses choses qui ne sont pas des commentairesE<nbsp>:

       (
         "           ##  Début d'une chaîne " ... "
         (
           \\.           ##  Caractère échappé
         |               ##    OU
           [^"\\]        ##  Non "\
         )*
         "           ##  Fin d'une chaîne " ... "

       |         ##     OU

         '           ##  Début d'une chaîne ' ... '
         (
           \\.           ##  Caractère échappé
         |               ##    OU
           [^'\\]        ##  Non '\
         )*
         '           ##  Fin d'une chaîne ' ... '

       |         ##     OU

         .           ##  Tout autre caractère
         [^/"'\\]*   ##  Caractères ne débutant pas un commentaire,
                     ##  une chaîne ou un échappement
       )
     }{$2}gxs;

Une légère modification retire aussi les commentaires C++ :

    s#/\*[^*]*\*+([^/*][^*]*\*+)*/|//[^\n]*|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#$2#gs;

 

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 --- à la possible exception de caractéristiques bizarres et expérimentales dans les versions de développement de Perl. Vous avez encore besoin d'utiliser des techniques autres pour analyser du texte bien équilibré, comme 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 le contourner ?

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 examiner 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 rationnelle :

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

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

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 extrêmement inefficace :

    # manière lente mais évidente
    @popstates = qw(CO ON MI WI MN);
    while (defined($line = <>)) {
        for $state (@popstates) {
            if ($line =~ /\b$state\b/i) {  
                print $line;
                last;
            }
        }
    }

C'est parce que Perl doit recompiler tous ces motifs pour chacune des lignes du fichier. À partir des versions 5.005, il existe une bien meilleure approche, utilisant le nouvel opérateur "qr//" :

    # utilise le magnifique et tout neuf opérateur qr//, avec même le
    # drapeau /i
    use 5.005;
    @popstates = qw(CO ON MI WI MN);
    @poppats   = map { qr/\b$_\b/i } @popstates;
    while (defined($line = <>)) {
        for $patobj (@poppats) {
            print $line if $line =~ /$patobj/;
        }
    }

 

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. 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 occurrences 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 rationnelle 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 le pouvez pas, une fois que vous les avez utilisées, utilisez-les comme vous voulez, car vous en avez déjà payé le prix. Souvenez-vous que certains algorithmes les apprécient vraiment. À partir de la version 5.005, la variable $& n'a plus le même coût que les deux autres.  

À quoi est bon \G dans une expression rationnelle ?

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 rationnelle à l'endroit où la dernière reconnaissance a eu lieu, c'est-à-dire l'endroit indiqué par pos(). Une correspondance ratée réinitialise la position de "\G" à moins que le modificateur "/c" ne soit actif.

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

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

Ou, en utilisant "\G", plus simple (et plus 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 conhérence, 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 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 ayez 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";

Il y a plusieurs double- (et multi-) octets codages couramment 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.  

Comment rechercher un motif fourni par l'utilisateur ?

Eh bien, si c'est vraiment un motif, alors utilisez juste

    chomp($pattern = <STDIN>);
    if ($line =~ /$pattern/) { }

Ou, puisque vous n'avez aucune garantie que votre utilisateur a entré une expression rationnelle valide, piégez l'exception de cette façon :

    if (eval { $line =~ /$pattern/ }) { }

Mais si vous voulez seulement chercher une chaîne et non pas un motif, alors vous devriez soit utiliser la fonction index(), qui est faite pour cela, soit, s'il est impossible de vous convaincre de ne pas utiliser une expression rationnelle pour autre chose qu'un motif, assurez-vous au moins d'utiliser "\Q"..."\E", documenté dans perlre.

    $pattern = <STDIN>;

    open (FILE, $input) or die "Couldn't open input $input: $!; aborting";
    while (<FILE>) {
        print if /\Q$pattern\E/;
    }
    close FILE;

 

AUTEUR ET COPYRIGHT

Copyright (c) 1997-1999 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.  

VERSION FRANÇAISE

Cette traduction française correspond à la version anglaise distribuée avec perl 5.6.0. Pour en savoir plus concernant ces traductions, consultez http://www.enstimac.fr/Perl/ .  

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).  

RELECTURE

Régis Julié (Regis.Julie@cetelem.fr) Roland Trique <roland.trique@uhb.fr> (mise à jour) Gérard Delafond
 

Index

NOM
DESCRIPTION
Comment utiliser les expressions régulières sans créer du code illisible et difficile à maintenir ?
J'ai des problèmes pour faire une reconnaissance sur plusieurs lignes. Qu'est-ce qui ne va pas ?
Comment extraire des lignes entre deux motifs qui sont chacun sur des lignes différentes ?
J'ai mis une expression rationnelle dans $/ mais cela ne marche pas. Qu'est-ce qui est faux ?
Comment substituer indépendamment de la casse de la partie gauche de la substitution, mais en préservant la casse de la partie droite ?
Comment faire pour que \w reconnaisse les caractères nationaux ?
Comment reconnaître une version locale-intelligente de /[a-zA-Z]/ ?
Comment protéger une variable pour l'utiliser dans une expression rationnelle ?
À quoi sert vraiment /o ?
Comment utiliser une expression rationnelle pour enlever les commentaires de type C d'un fichier ?
Est-ce possible d'utiliser les expressions régulières de Perl pour reconnaître du texte bien équilibré ?
Que veut dire les expressions régulières sont gourmandes ? Comment puis-je le contourner ?
Comment examiner chaque mot dans chaque ligne ?
Comment afficher un rapport sur les fréquences de mots ou de lignes ?
Comment faire une reconnaissance approximative ?
Comment reconnaître efficacement plusieurs expressions régulières en même temps ?
Pourquoi les recherches de limite de mot avec \b ne marchent pas pour moi ?
Pourquoi l'utilisation de $&, $`, or $' ralentit tant mon programme ?
À quoi est bon \G dans une expression rationnelle ?
Les expressions régulières de Perl sont-elles AFD ou AFN ? Sont-elles conformes à POSIX ?
Qu'est ce qui ne va pas avec l'utilisation de grep ou map dans un contexte vide ?
Comment reconnaître des chaînes avec des caractères de plusieurs octets ?
Comment rechercher un motif fourni par l'utilisateur ?
AUTEUR ET COPYRIGHT
VERSION FRANÇAISE
TRADUCTION
RELECTURE

This document was created by man2html, using the manual pages.
Time: 07:52:28 GMT, March 13, 2003