2009-12-02 23 views
7

¿Qué expresión regular puedo usar para encontrar todas las cadenas bar no van precedidas de la cadena foo? Tener espacios en blanco entre los dos también es ilegal.¿Cómo encontrar una palabra NO precedida por otra palabra específica?

así, el texto debe coincidir con las siguientes cadenas

foo is bar 
hello bar 

Pero no estas

foobar 
foo  bar 

He intentado usar el siguiente

(?!<foo)bar 

y se hace el trabajo realizado por eliminando foobar, pero necesito ocuparme de los espacios en blanco, y por supuesto

(?!<foo)\s*bar 

coincide con todas las cadenas.

Gracias!

+1

"coincide con todas las cadenas". - modo pedante: (?!

+0

¡Tienes razón, gracias por señalar eso! Terminé usando lo siguiente: preg_match ('/ (foo)? \ S * bar /', haystack, coincidencias); que encontrará la barra (ya sea precedida por foo o no), y luego una comprobación rápida de coincidencias [] identificará si un foo estuvo allí o no. – Sleepster

+0

Lo que está buscando se llama específicamente ** aseveración de búsqueda negativa de ancho cero **. Perl, en particular, no es compatible con el ancho de mira variable (positivo o negativo), por lo que cosas como \ s * dentro de uno de ellos no funcionarán. Intente utilizar operadores de coincidencia múltiple en su lugar. – fennec

Respuesta

0
(?!<foo)\s*bar 

Esto coincidirá con el espacio en blanco

+0

Uh no. Primero, es '(?

+2

seguro de que todo lo que sé es que JA editó mi respuesta, me siento bendecido. – Hogan

0

php:

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

Perl:

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

Como se mencionó en la pregunta original, esto no funciona. – Sleepster

+0

Ah, sí, porque técnicamente se puede encontrar que todas las cadenas tienen cadenas que no son foo antes que la barra ... –

+0

Lo que realmente necesitas es hacer una expresión regular negativa. $ string! ~/foo \ s * bar /. Actualizado con versiones php y perl. –

2

dado algunos casos de prueba

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

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

se podía hacer, por supuesto, por la alimentación de los resultados de un patrón a otro:

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

También podemos hacerlo con uno:

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

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

Pero no tome mi palabra para ella.

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

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

Impresionante que tienes esto para trabajar. Lo más probable es que "foo" y "bar" sean solo marcadores de posición para cadenas mucho más largas. Parece que tus expresiones regulares serán extremadamente largas para cualquier ejemplo del mundo real. +1 para el enfoque diferente sin embargo. –

+0

Gracias, y la triste noticia es que un patrón literal es el mejor de los casos. Me pregunto cuál es el límite de este enfoque. Sería bueno para tales tareas tener un modificador de expresión regular que complementa el estado de aceptación de cada estado NFA. –

4

Es mejor utilizar otras funciones del lenguaje de programación que buscar demasiado para un patrón de expresiones regulares.

Está buscando cadenas para las que $s =~ /bar/ and not $s =~ /foo\s*bar/ sea cierto.

El resto del script a continuación es solo para pruebas.

#!/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"; 
    } 
} 

de salida:

 
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

¿No concuerda eso incluso si la cadena no contiene 'barra'? –

+0

@Mark Byers: Gracias por señalar mi supervisión. Fijo. –

+1

'bar foobar' también es un caso de prueba interesante. Sin embargo, no estoy seguro de cuál es la salida esperada. –

0

Tomando la información de las respuestas anteriores, envolviendo como Perl de una sola línea, y haciendo caso-insensible las expresiones regulares.

de 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 

Con xx.txt que contiene:

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

El resultado de ejecutar la de una sola línea en la línea de comando:

foo is bar 
hello bar 
barbar 
bar foo 
barfoo 
Cuestiones relacionadas