2009-12-02 8 views
7

Quelle expression régulière puis-je utiliser pour rechercher toutes les chaînes bar ne sont pas précédées de la chaîne foo? Avoir des espaces entre les deux est également illégal.Comment trouver un mot non précédé d'un autre mot spécifique?

Ainsi, le regex doit correspondre aux chaînes suivantes

foo is bar 
hello bar 

Mais pas ces

foobar 
foo  bar 

J'ai essayé d'utiliser la

suivante
(?!<foo)bar 

et il obtient le travail accompli pour éliminer foobar, mais je dois prendre soin de l'espace, et bien sûr

(?!<foo)\s*bar 

correspond à toutes les chaînes.

Merci!

+1

"correspond à toutes les chaînes." - mode pédant: (?!

+0

Vous avez raison, merci de l'avoir signalé! J'ai fini par utiliser ce qui suit: preg_match ('/ (foo)? \ S * bar /', haystack, matches); qui trouvera la barre (précédée ou non par foo), puis une vérification rapide des correspondances [] permettra d'identifier si un truc était là ou non. – Sleepster

+0

La chose que vous recherchez est spécifiquement appelée ** assertion de regard négatif derrière la largeur **. Perl, notamment, ne supporte pas look-behind à largeur variable (positive ou négative), donc des choses comme \ s * dans l'une d'entre elles ne fonctionneront pas. Essayez d'utiliser plusieurs opérateurs de correspondance à la place. – fennec

Répondre

0
(?!<foo)\s*bar 

Cela correspondra les espaces blancs

+0

Uh no. Tout d'abord, c'est '(?

+2

sûr que tout ce que je sais est JA édité ma réponse, je me sens béni. – Hogan

0

php:

!preg_match(/foo\s*bar/,$string) && preg_match(/bar/,$string) 

perl:

$string !~ /foo\s*bar/ && $string =~ /bar/ 
+0

Comme mentionné dans la question initiale, cela ne fonctionne pas. – Sleepster

+0

Ah, oui, parce que techniquement, toutes les chaînes peuvent être trouvées avec des chaînes non-foo avant la barre ... –

+0

Ce dont vous avez vraiment besoin, c'est de faire une regex négative. $ string! ~/foo \ s * bar /. Mise à jour avec les versions php et perl. –

2

Compte tenu de quelques cas de test

my @match = (
    "foo is bar", 
    "hello bar", 
); 

my @reject = (
    "foobar", 
    "foo  bar", 
); 

vous pouvez bien sûr faire en nourrissant les résultats d'un modèle à l'autre:

my @control = grep !/foo\s*bar/, grep /bar/ => @match, @reject; 

Nous pouvons aussi le faire avec un:

my $nofoo = qr/ 
    (  [^f] | 
    f (?! o) | 
    fo (?! o \s* bar) 
)* 
/x; 

my $pattern = qr/^ $nofoo bar /x; 

Mais ne prenez pas ma parole.

for (@match) { 
    print +(/$pattern/ ? "PASS" : "FAIL"), ": $_\n"; 
} 

for (@reject) { 
    print +(/$pattern/ ? "FAIL" : "PASS"), ": $_\n"; 
} 
+0

Impressionnant que vous avez eu ce travail. Très probablement "foo" et "bar" sont juste des espaces réservés pour des chaînes beaucoup plus longues. Il semble que vos expressions régulières deviendront extrêmement longues pour tous les exemples du monde réel. +1 pour l'approche différente cependant. –

+0

Merci, et la triste nouvelle est qu'un modèle littéral est le meilleur des cas. Je me demande quelle est la limite de cette approche. Ce serait bien que de telles tâches aient un commutateur d'expression régulière qui complète le statut d'acceptation de chaque état NFA. –

4

Mieux vaut utiliser d'autres fonctions du langage de programmation que d'avoir trop tendance à rechercher un motif regex.

Vous recherchez des chaînes pour lesquelles $s =~ /bar/ and not $s =~ /foo\s*bar/ est vraie.

Le reste du script ci-dessous est juste à tester.

#!/usr/bin/perl 

use strict; use warnings; 

my %strings = (
    'foo is bar' => 1, 
    'hello bar' => 1, 
    'foobar'  => 0, 
    'foo  bar' => 0, 
    'barbar'  => 1, 
    'bar foo'  => 1, 
    'foo foo'  => 0, 
); 

my @accept = grep { $strings{$_} } keys %strings; 
my @reject = grep { not $strings{$_} } keys %strings; 

for my $s (@accept) { 
    if ($s =~ /bar/ and not $s =~ /foo\s*bar/) { 
     print "Good: $s\n"; 
    } 
    else { 
     print "Bad : $s\n"; 
    } 
} 

for my $s (@reject) { 
    if ($s =~ /bar/ and not $s =~ /foo\s*bar/) { 
     print "Bad : $s\n"; 
    } 
    else { 
     print "Good: $s\n"; 
    } 
} 

sortie:

 
E:\srv\unur> j 
Good: bar foo 
Good: hello bar 
Good: foo is bar 
Good: barbar 
Good: foo foo 
Good: foo  bar 
Good: foobar 
+0

Cela ne correspondra-t-il pas même si la chaîne ne contient pas de 'barre'? –

+0

@Mark Byers: Merci d'avoir signalé mon erreur. Fixé. –

+1

'bar foobar' fait également un cas de test intéressant. Je ne suis pas sûr de ce que la sortie attendue est ici. –

0

Prenant les informations de réponses précédentes, l'emballage en tant que perl one-liner, et en faisant les expressions régulières insensibles à la casse.

Windows:

perl -lne "print $_ if $_ !~ m/foo\s*bar/i && $_ =~ m/bar/i;" c:\temp\xx.txt 

Linux:

perl -lne 'print $_ if $_ !~ m/foo\s*bar/i && $_ =~ m/bar/i;' /tmp/xx.txt 

Avec xx.txt contenant:

foo is bar 
hello bar 
foobar 
foo  bar 
barbar 
bar foo 
barfoo 
foo foo 

Le résultat de l'exécution de la seule ligne à l'invite de commande:

foo is bar 
hello bar 
barbar 
bar foo 
barfoo