web-dev-qa-db-fra.com

Expression rationnelle négative pour correspondance de modèle de chaîne Perl

J'ai cette regex:

if($string =~ m/^(Clinton|[^Bush]|Reagan)/i)
  {print "$string\n"};

Je veux faire correspondre Clinton et Reagan, mais pas Bush.

Ça ne fonctionne pas.

43
joe

Exemple de texte:

Clinton a dit
Bush a utilisé des crayons
Reagan a oublié

En omettant simplement un match de brousse:

$ Perl -ne 'print if /^(Clinton|Reagan)/' textfile
Clinton said
Reagan forgot

Ou si vous voulez vraiment préciser:

$ Perl -ne 'print if /^(?!Bush)(Clinton|Reagan)/' textfile
Clinton said
Reagan forgot
27
Demosthenex

Votre regex ne fonctionne pas parce que [] définit une classe de caractères, mais vous voulez un lookahead:

(?=) - Positive look ahead assertion foo(?=bar) matches foo when followed by bar
(?!) - Negative look ahead assertion foo(?!bar) matches foo when not followed by bar
(?<=) - Positive look behind assertion (?<=foo)bar matches bar when preceded by foo
(?<!) - Negative look behind assertion (?<!foo)bar matches bar when NOT preceded by foo
(?>) - Once-only subpatterns (?>\d+)bar Performance enhancing when bar not present
(?(x)) - Conditional subpatterns
(?(3)foo|fu)bar - Matches foo if 3rd subpattern has matched, fu if not
(?#) - Comment (?# Pattern does x y or z)

Alors essayez: (?! Bush)

132
Stuck

Votre regex dit ce qui suit:

/^         - if the line starts with
(          - start a capture group
Clinton|   - "Clinton" 
|          - or
[^Bush]    - Any single character except "B", "u", "s" or "h"
|          - or
Reagan)   - "Reagan". End capture group.
/i         - Make matches case-insensitive 

Donc, en d'autres termes, la partie centrale de votre regex vous fait foirer. S'agissant d'un groupe "fourre-tout", il autorisera toute ligne qui ne commence par aucune des majuscules ou minuscules de "Bush". Par exemple, ces lignes correspondraient à votre regex:

Our president, George Bush
In the news today, pigs can fly
012-3123 33

Soit vous faites une prévision négative, comme suggéré précédemment, soit vous créez simplement deux regex:

if( ($string =~ m/^(Clinton|Reagan)/i) and
    ($string !~ m/^Bush/i) ) {
   print "$string\n";
}

Comme mirod a souligné dans les commentaires, la seconde vérification est plutôt inutile lors de l'utilisation du curseur (^) pour ne faire correspondre que le début des lignes, car les lignes commençant par "Clinton" ou "Reagan" ne peuvent jamais commencer par "Bush".

Cependant, il serait valable sans les carets.

17
TLP

Quel est le problème avec l'utilisation de deux regex (ou trois)? Cela rend vos intentions plus claires et peut même améliorer votre performance:

if ($string =~ /^(Clinton|Reagan)/i && $string !~ /Bush/i) { ... }

if (($string =~ /^Clinton/i || $string =~ /^Reagan/i)
        && $string !~ /Bush/i) {
    print "$string\n"
}
3
mob

Si ma compréhension est correcte, alors vous voulez faire correspondre toute ligne qui a Clinton et Reagan, dans n'importe quel ordre, mais pas Bush. Comme suggéré par Stuck, voici une version avec des assertions d'anticipation:

#!/usr/bin/Perl

use strict;
use warnings;

my $regex = qr/
    (?=.*clinton)  
    (?!.*bush) 
    .*reagan       
    /ix;

while (<DATA>) {
    chomp;
    next unless (/$regex/);
    print $_, "\n";
}


__DATA__
shouldn't match - reagan came first, then clinton, finally bush
first match - first two: reagan and clinton
second match - first two reverse: clinton and reagan
shouldn't match - last two: clinton and bush
shouldn't match - reverse: bush and clinton
shouldn't match - and then came obama, along comes mary
shouldn't match - to clinton with Perl

Résultats

first match - first two: reagan and clinton
second match - first two reverse: clinton and reagan

comme vous le souhaitez, il correspond à n'importe quelle ligne qui a Reagan et Clinton dans n'importe quel ordre.

Vous voudrez peut-être essayer de lire le fonctionnement des assertions lookahead avec des exemples à l'adresse http://www252.pair.com/comdog/mastering_Perl/Chapters/02.advanced_expressions.html

ils sont très savoureux :)

2
Ashish Kumar