Apprivoisez votre texte avec Perl


  • FrançaisFrançais


  • Bien que sa popularité ait été tempérée par des langages comme Python, Lua et Go, Perl a été l’un des principaux langages utilitaires sous Unix et Linux pendant 30 ans. Il reste aujourd’hui un composant important et puissant dans de nombreux systèmes open source. Si vous n’avez pas beaucoup utilisé Perl, vous serez peut-être surpris de voir à quel point il peut être utile pour de nombreuses tâches. Cela est particulièrement vrai si vous traitez de grandes quantités de texte dans votre travail quotidien.

    Si vous avez besoin d’un langage qui vous permette de rechercher et de manipuler rapidement et facilement de gros volumes de texte, Perl est difficile à battre. En fait, faire exactement cela est ce pour quoi Larry Walls a initialement construit le langage.

    Si vous débutez avec Perl, vous pouvez lire cette introduction rapide à Perl pour avoir une idée des bases.

    Recherche de texte avec regex

    Pour commencer, voici un exemple de script d’expression régulière simple (parfois abrégé en “regex”).

    Supposons que vous ayez une liste de noms dans un fichier appelé names.txt:

    Steve Smith
    Jane Murphy
    Bobby Jones
    Elizabeth Arnold
    Michelle Swanson

    Vous voulez retirer toutes les personnes nommées Elizabeth. Mettez l’expression régulière que vous recherchez – ici c’est “Elizabeth” – entre des barres obliques, et Perl regardera chaque ligne suivant le jeton spécial DATA et n’imprimera que les lignes qui correspondent.

    use warnings;
    use strict;

    open my $fh, '<:encoding(UTF-8)', "$names.txt" or
      die "Could not read file\n";

    while(<$fh>){
      print if /Elizabeth/;
    }

    Une note rapide concernant ce code : l’expression régulière doit se trouver à la fin de la ligne. Alors if /Elizabeth/ print; ne fonctionnera pas. Cette erreur est courante pour les nouveaux programmeurs Perl.

    Modification des mots sélectionnés avec lookarounds

    Parfois, vous ne voudrez peut-être pas faire quelque chose avec chaque instance d’une chaîne, mais faites plutôt vos sélections en fonction de ce qui se trouve avant ou après la chaîne. Par exemple, vous souhaitez peut-être remplacer la chaîne « Robert » par « Bob », mais uniquement si « Robert » est suivi de « Dylan ». Sinon, vous ne voulez pas changer le nom.

    Pour Perl, c’est facile. Vous pouvez appliquer cette condition avec une seule ligne de code directement depuis votre terminal :

    perl -i.bkp -pe 's/Robert (?=Dylan)/Bob /g' names.txt

    Pour ceux qui découvrent Perl, cette ligne peut sembler un peu intimidante à première vue, mais elle est vraiment très simple et élégante.

    le -i flag fait que la sortie du programme est réécrite dans un fichier au lieu de s’afficher sur l’écran du terminal. Vous pouvez fournir une extension à -i pour enregistrer le fichier d’entrée dans un fichier avec l’extension donnée. En d’autres termes, je crée une sauvegarde du fichier d’origine avec le .bkp extension. (Assurez-vous de ne pas mettre d’espace entre -i et la rallonge .bkp.)

    Après cela, j’utilise le -pe options. le -e L’option me permet d’exécuter Perl à partir de la ligne de commande. le -p L’option fait que mon code parcourt chaque ligne du fichier et imprime la sortie. Après tout, je veux que le nouveau fichier contienne tous les noms du fichier original, pas seulement celui de M. Dylan.

    Vient ensuite la phrase s/Robert (?=Dylan)/Bob /g.

    Ici, je remplace (indiqué par s) ce qui se trouve entre les deux premières barres obliques avec ce qui se trouve entre la deuxième et la troisième barre oblique. Dans ce cas, je veux substituer “Bob” à “Robert” dans une circonstance spécifique. Je veux faire cela pour chaque instance du fichier, pas seulement la première qu’il trouve, donc j’utilise le g drapeau pour global à la fin.

    Qu’en est-il de cet aspect étrange (?=Dylan)? C’est ce qu’on appelle un anticipation positive dans le monde des expressions régulières. Il n’est pas capturant, il ne sera donc remplacé par rien (Bob, dans cet exemple) ; au lieu de cela, l’expression réduit les résultats qui sont modifiés.

    Je cherche la chaîne “Robert” si et seulement si il est suivi (c’est une anticipation positive) par la chaîne “Dylan”.

    Sinon, ignorez-le. Si le nom “Robert Smith” est dans ma liste de noms, par exemple, je veux le laisser seul et ne pas le changer en “Bob Smith”.

    Voici les contournements disponibles pour les utilisateurs de Perl :

    • anticipation positive : ?=pattern
    • anticipation négative : ?!pattern
    • regard en arrière positif : ?<=pattern
    • arrière-plan négatif : ?<!pattern

    Assurez-vous de placer des regards en arrière derrière la chaîne que vous recherchez. Pour changer “Sam” en “Samantha”, mais seulement si “Miss” le précède, vous écrivez :

    s/(?<=Miss) Sam/Samantha/g'

    Capturez ce qui précède ou suit un mot

    Que se passe-t-il si vous voulez tout obtenir avant ou après un mot, mais vous ne savez pas combien de mots cela fera ? Perl rend cela rapide et facile.

    Cet exemple commence par une liste de matchs de baseball récents (fictifs) avec l’équipe gagnante en premier, suivie du mot “plus”, suivi de l’équipe non gagnante et du score final.

    San Francisco Giants over Miami Marlins 3:0
    Chicago Cubs over Houston Astros 6:1
    New York Mets over San Francisco Giants 4:3

    Perl a quelques variables intégrées spéciales :

    • $& (esperluette dollar) contient la dernière chaîne capturée
    • $` (dollar backtick) contient ce qui précède la chaîne capturée sur la ligne
    • $'(apostrophe dollar) contient ce qui vient de la chaîne capturée sur la ligne

    Pour obtenir une liste des équipes qui ont gagné, je dois capturer le mot “over” et ensuite afficher tout ce qui précède.

    use strict;
    use warnings;

    while (<DATA>){
            /over/;
            print "$`\n";

    }

    Se déplacer dans un fichier avec la fonction de recherche

    Jusqu’à présent, tous les programmes que j’ai mentionnés ont commencé en haut et se sont poursuivis ligne par ligne jusqu’à ce qu’ils atteignent la fin, moment auquel le programme est terminé. C’est souvent tout ce dont vous avez besoin, mais parfois vous voulez vous déplacer dans un programme pour effectuer des tâches spécifiques dans un certain ordre.

    Dans ce cas, Perl seek la fonction est ce que vous recherchez.

    le seek La fonction prend trois arguments : un descripteur de fichier, un décalage d’octet et une position de fichier.

    La position du fichier peut être l’une des trois valeurs suivantes :

    • 0 = début du fichier
    • 1 = position actuelle dans le fichier
    • 2 = fin du fichier

    Le deuxième argument, le décalage d’octet, est le nombre d’octets à partir de la position du fichier vers laquelle vous voulez vous rendre.

    Les nombres positifs déplacent la position du curseur vers la droite, tandis que les valeurs négatives déplacent le curseur vers la gauche. Comme il n’y a rien avant le début, vous ne pouvez utiliser des décalages d’octets négatifs que si la position du fichier est un 1 ou un 2.

    Voici un exemple pour clarifier tout cela :

    Supposons que vous ayez une longue liste de noms avec l’anniversaire de la personne. Vous souhaitez créer une nouvelle liste avec les personnes dont l’anniversaire est en août répertoriées en haut, suivies de toutes les autres.

    Pour ce faire, vous devez parcourir toute la liste, en trouvant tout le monde avec un anniversaire en août. Ensuite, une fois que vous atteignez le bas de la liste, vous devez revenir en haut et obtenir toutes les personnes dont l’anniversaire n’est pas en août.

    Voici une partie du fichier d’origine :

    Bob Smith 03/12/1967
    Carl Carlson 01/22/1998
    Susan Meyers 01/28/1980
    Derek Jackson 08/02/2009
    Sara Miller 02/11/2002
    Marcus Philips 08/28/1999
    Jeremy Stills 11/30/2001

    Voici un script Perl pour accomplir la tâche :

    use strict;
    use warnings;

    open my $fh, '<:encoding(UTF-8)', "originalfile.txt" or
        or die "Error opening file: $!d\n";

    while($line = <$fh>){
      if ($line =~ m#\t\t08/#){
        print "$line\n";
      }

    seek ($fh, 0, 0);

    while (<$fh>){
      if ($line !~ m#\t\t08/#){
        print "$line";
      }

    close $fh;

    La ligne if ($line =~ m#\t\t08/#) { utilise le m flag pour la recherche d’expressions régulières, vous permettant d’utiliser un délimiteur arbitraire dans votre recherche.

    La valeur par défaut, comme vous l’avez peut-être déjà remarqué, est une barre oblique (/). Mais comme les barres obliques sont utilisées dans les dates, cela peut perturber la recherche. Heureusement, Perl vous permet d’utiliser un délimiteur différent en plaçant votre choix après le m drapeau. Dans cet exemple, j’utilise le hachage alternatif commun (#), mais vous pouvez utiliser d’autres caractères (par exemple, des crochets, une esperluette, un X majuscule, etc.) tant qu’ils n’interfèrent pas avec votre requête.

    Dans ce cas, vous recherchez deux caractères de tabulation, écrits comme \t\t dans cet exemple. Il aurait pu aussi s’écrire \t{2}.

    Les caractères de tabulation doivent être suivis d’un 0suivie par an 8 (Août est le huitième mois), suivi d’une barre oblique. Notez que vous ne pouvez pas simplement rechercher 08, car cela correspondrait également aux personnes nées le huitième jour de n’importe quel mois et aux personnes nées en 2008.

    Une fois que Perl a trouvé et imprimé tous les anniversaires d’août, j’utilise le seek fonction pour revenir au début du fichier. En parcourant le fichier une seconde fois, la recherche de regex passe d’une correspondance (=~) à une non-correspondance (!~) pour que tout le monde soit né au cours de l’un des 11 autres mois.

    Expliquer les regex aux autres

    Les expressions régulières, en Perl et dans de nombreux autres langages, sont une chose merveilleuse à connaître et à utiliser.

    Ils peuvent transformer ce qui serait autrement un processus long et déroutant à programmer en une simple expression de quelques caractères seulement. Mais ils ont la réputation d’être parfois un peu énigmatiques.

    Écrire une expression régulière longue et compliquée peut rendre les programmeurs fiers d’eux-mêmes, mais il n’y a pas de place pour du code inutilement compliqué. Un signe d’un bon programmeur est que les autres programmeurs peuvent facilement comprendre ce qu’ils font.

    Lorsque vous écrivez autre chose qu’une expression régulière relativement basique, c’est généralement une bonne idée de commenter votre regex avec le x option. Cette option permet à Perl d’ignorer les commentaires et les espaces dans la regex afin que vous puissiez expliquer à votre futur moi et aux autres ce que vous tentiez de faire.

    Remarque : la question à se poser n’est pas « pourrais-je comprendre ce que fait la regex, même sans commentaires », mais « devrais-je demander aux autres de le comprendre ». N’obligez pas les autres à essayer de comprendre ce que vous faites.

    Comparez les deux exemples de code ci-dessous. Ils font tous les deux la même chose, mais la deuxième version est plus facile à comprendre.

    Supposons que vous disposiez de ces exemples de données pour effectuer une recherche :

    01/21/1998
    sample text
    Sept/21/97
    Here is another line
    Mr. Smith
    01-12-2009
    7/23/1998
    Fake text
    Feb./5/09

    Et vous avez l’expression régulière suivante dans votre script Perl :

    m%(?<![-"https://opensource.com/"\d])((\d\d?)|[A-Z][a-z]*\.?)(?=[-|/])(/|-)\d\d?(/|-)\d{2,4}%

    Pouvez-vous jeter un coup d’œil à cela et comprendre ce qu’il fait? Probablement pas. Vous pourriez être en mesure de le comprendre, mais cela prendrait quelques minutes.

    D’autre part, vous pourriez écrire la même expression régulière comme ceci :

    use strict;
    use warnings;

    while (<DATA>){
      print if m%   # capture dates written in multiple formats
      (?<![-/\d])   # is not preceded by a hyphen, slash, or digit
      ((\d\d?)|[A-Z][a-z]*\.?)(?=[-/])  # month 1 or 2 digits, or word with optional hyphen
                                        # followed by a hyphen or slash
      (/|-)\d\d?    # 1 or 2 digit day
      (/|-)\d{2,4}  # 2 or 4 digit year
      %x;
    }

    Cette version indique clairement que nous recherchons des dates.

    Le mois apparaît sous la forme d’un ou de deux chiffres, écrit sous forme de mot ou abrégé avec ou sans point, suivi d’une barre oblique ou d’un trait d’union. Le jour est écrit sous la forme d’un ou de deux chiffres, suivi d’une barre oblique ou d’un trait d’union, suivi de l’année, écrite sous la forme d’une année à quatre chiffres ou d’une année à deux chiffres.

    En utilisant le x modificateur dans l’expression régulière oblige Perl à ignorer les espaces et les commentaires, ce qui permet d’expliquer l’expression régulière de manière plus conviviale. Notez que dans cet exemple, comme précédemment, j’ai également utilisé le m modificateur pour changer les délimiteurs de regex par défaut / à % car cette expression régulière contient des barres obliques.

    Conclusion

    J’espère que cet article vous a donné un avant-goût de la façon dont le langage Perl peut accélérer certains de vos problèmes de codage textuel et vous faciliter la tâche. Perl est un langage mature et riche ; cette introduction a à peine effleuré la surface de ce qu’elle peut faire. Si vous souhaitez augmenter votre productivité en tant que programmeur, Perl vaut le détour.

    Source

    La Rédaction

    L'équipe rédactionnnelle du site

    Pour contacter personnellement le taulier :

    Laisser un commentaire

    Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

    Copy code