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.
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
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)
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.
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"
}
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 :)