2008-10-29 14 views
23

Buscando un poco de ayuda de expresiones regulares. me gustaría diseñar una expresión que coincide con una cadena con "foo " O " bar", pero no ambos " foo" Y "barra"Exclusivo O en expresión regular

Si hago algo así. ..

/((foo)|(bar))/ 

va coincide con "foobar ". No es lo que estoy buscando. Entonces, ¿cómo puedo hacer corresponder regex solo cuando un término o el otro está presente?

Gracias!

+0

¿Sería foofoobar sea un partido, ya que contiene " foo "y" foobar "? ¿Qué tal "foonbar"?¿Podría proporcionar ejemplos de coincidencias y no coincidencias? –

+0

Partidos: "foo", "bar" nonmatches: "foofoo" "barfoo" "foobarfoo" "barbar" "barfoofoo" – SocialCensus

+2

Si usted no quiere "foofoo" a la altura, entonces' No estoy hablando realmente de una exclusiva o. – cjm

Respuesta

8

Usted puede hacer esto con una sola expresión regular pero yo sugeriría para facilitar la lectura haces algo como ...

(/foo/ and not /bar/) || (/bar/ and not /foo/) 
+0

De hecho, estoy bastante seguro de que pondría la lógica XOR en el código en sí, y no en la expresión regular. – Pistos

+1

O mejor aún,/foo/xor/bar /, si su idioma tiene un operador XOR. (Perl lo hace). – cjm

+0

Esta parece ser la mejor solución, ¡Gracias! – SocialCensus

0

que haría uso de algo como esto. Solo busca espacio alrededor de las palabras, pero puede usar \b o \B para buscar un borde si usa \w. Esto coincidiría con "foo" o "bar", así que obviamente también deberías reemplazar el espacio en blanco, por las dudas. (Suponiendo que está reemplazando algo.)

/\s((foo)|(bar))\s/ 
0

No creo que se pueda hacer con una sola expresión regular. Y los límites pueden funcionar o no, dependiendo de con qué te encuentres.

Me gustaría hacer coincidir cada expresión regular por separado, y hacer un XOR en los resultados.

foo = re.search("foo", str) != None 
bar = re.search("bar", str) != None 
if foo^bar: 
    # do someting... 
0

he intentado con Regex Coach contra:

x foo y 
x bar y 
x foobar y 

Si puedo comprobar la opción g, hecho que coincide con las tres palabras, porque se busca de nuevo después de cada partido.
Si no desea este comportamiento, se puede anclar la expresión, por ejemplo, a juego sólo en los límites de palabra:

\b(foo|bar)\b 

dar más contexto en el problema (lo que los datos se parece a) podría dar mejores respuestas.

15

Si su idioma de expresiones regulares tiene, usar negative lookaround:

(?<!foo|bar)(foo|bar)(?!foo|bar) 

Esto corresponderá con "foo" o "bar" que no está precedido o seguido inmediatamente por "foo" o "barra", que creo que es lo que querías.

No está claro, a partir de su pregunta o ejemplos, si la cadena que intenta hacer corresponder puede contener otros tokens: "foocuzbar". Si es así, este patrón no funcionará.

Éstos son los resultados de los casos de prueba ("verdadero" significa que el patrón se encuentra en la entrada):

foo: true 
bar: true 
foofoo: false 
barfoo: false 
foobarfoo: false 
barbar: false 
barfoofoo: false 
+2

Gracias por enseñarme sobre lookarounds negativos :) – SocialCensus

+0

esto funciona perfectamente pero no en Perl, fyi – Keng

+0

Para mí la mejor respuesta aquí. Funciona bien en la página de prueba https://regex101.com – Ralf

0

Utilizando los límites de palabras, se puede obtener la palabra ...

[email protected] ~ 
$ echo "Where is my bar of soap?" | egrep "\bfoo\b|\bbar\b" 
Where is my bar of soap? 

[email protected] ~ 
$ echo "What the foo happened here?" | egrep "\bfoo\b|\bbar\b" 
What the foo happened here? 

[email protected] ~ 
$ echo "Boy, that sure is foobar\!" | egrep "\bfoo\b|\bbar\b" 
2

No ha especificado el comportamiento con respecto al contenido que no sea "foo" y "bar" o las repeticiones de uno en ausencia del otro. por ejemplo, ¿Debe "foo d" o "barra de bar ian" coincidencias?

Suponiendo que desea hacer coincidir cadenas que contengan solo una instancia de "foo" o "bar", pero no las dos y no varias instancias de la misma, sin importar nada más en la cadena (es decir, " "fósforos" y "bárbaro" no coinciden), entonces podría usar una expresión regular que devuelva el número de coincidencias encontradas y solo considerarla exitosa si se encuentra exactamente una coincidencia. por ejemplo, en Perl:

@matches = ($value =~ /(foo|bar)/g) # @matches now hold all foos or bars present 
if (scalar @matches == 1) {   # exactly one match found 
    ... 
} 

Si se permite que múltiples repeticiones de la misma diana (es decir, "bárbaro" partidos), entonces este mismo enfoque general podría ser utilizado por luego caminar la lista de coincidencias para ver si los partidos son todas repeticiones del mismo texto o si la otra opción también está presente.

1

Si quiere una verdadera exclusiva o, simplemente lo haría en código en lugar de en la expresión regular. En Perl:

/foo/ xor /bar/ 

Pero su comentario:

Partidos: "foo", nonmatches "bar": "foofoo" "barfoo" "foobarfoo" "barbar" "barfoofoo"

indica que no está realmente buscando exclusivas o. En realidad quieres decir "¿El /foo|bar/ coincide exactamente una vez?"

my $matches = 0; 
while (/foo|bar/g) { 
    last if ++$matches > 1; 
} 

my $ok = ($matches == 1) 
26

Esto es lo que yo uso:

/^(foo|bar){1}$/ 

Ver: http://www.regular-expressions.info/quickstart.html bajo la repetición

+1

Solución mucho más elegante que la respuesta aceptada, especialmente cuando tienes más de 2 casos ... –

+0

¿Por qué agregaste '{1}', ¿qué significa eso? – oriadam

+0

Esto está mal, solo significa que 'foo' o' bar' deben coincidir solo una vez. – Karl

6

Esto tomará 'foo' y 'bar', pero no 'foobar' y no 'blafoo' y no 'Blåbär':

/^(foo|bar)$/ 

^ = mark start of string (or line) 
$ = mark end of string (or line) 

Esto tomará 'foo' y 'bar' y 'foo bar' y 'bar-foo', pero no 'parametro' y No 'blafoo' y no 'Blåbär':

/\b(foo|bar)\b/ 

\b = mark word boundry 
0

Sé que esto es una entrada tardía, pero sólo para ayudar a otros que pueden estar buscando:

(/b(?:(?:(?!foo)bar)|(?:(?!bar)foo))/b)