Pregunta

Buscando un poco de ayuda de expresiones regulares. Me gustaría diseñar una expresión que coincida con una cadena con " foo " O " barra " ;, pero no ambos " foo " Y " barra "

Si hago algo como ...

/((foo)|(bar))/

Coincidirá con " foobar " ;. No es lo que estoy buscando. Entonces, ¿cómo puedo hacer que las expresiones regulares coincidan solo cuando hay un término u otro presente?

¡Gracias!

¿Fue útil?

Solución

Puedes hacer esto con una sola expresión regular, pero te sugiero que, para facilitar la lectura, hagas algo como ...

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

Otros consejos

Esto es lo que uso:

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

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

Si su lenguaje de expresiones regulares lo admite, use lookaround negativo :

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

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

No queda claro en tu pregunta o en los ejemplos si la cadena que intentas emparejar puede contener otros tokens: " foocuzbar " ;. Si es así, este patrón no funcionará.

Aquí están los resultados de sus casos de prueba (" verdadero " significa que el patrón se encontró en la entrada):

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

Esto tomará 'foo' y 'bar' pero no 'foobar' y no 'blafoo' y no 'blabar':

/^(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 'foobar' y no 'blafoo' y no 'blabar':

/\b(foo|bar)\b/

\b = mark word boundry

No ha especificado ningún comportamiento con respecto al contenido que no sea " foo " y " barra " o repeticiones de uno en ausencia del otro. por ejemplo, debería " foo d " o " barbar ian " partido?

Suponiendo que desea hacer coincidir cadenas que contienen solo una instancia de " foo " o " bar " ;, pero no ambas y no múltiples instancias de la misma, sin tener en cuenta nada más en la cadena (es decir, " coincidencias " comida " barbarian " no coincide)), entonces puede usar una expresión regular que devuelve el número de coincidencias encontradas y solo lo considera exitoso 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 permiten repeticiones múltiples de ese mismo objetivo (es decir, coincidencias de " bárbaro "), este mismo enfoque general se puede usar al recorrer la lista de coincidencias para ver si todas las coincidencias son repeticiones del mismo texto si la otra opción también está presente.

Es posible que desee considerar el? prueba condicional.

(?(?=regex)then|else)

Condicionales de expresiones regulares

Si desea 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 tu comentario:

  

Coincidencias: " foo " ;, " barra " no coinciden:   " foofoo " " barfoo " " foobarfoo " " barbar "   " barfoofoo "

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

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

my $ok = ($matches == 1)

Sé que esta es una entrada tardía, pero solo para ayudar a otros que puedan estar buscando:

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

Yo usaría algo como esto. Simplemente busca el espacio alrededor de las palabras, pero puede usar \ b o \ B para verificar si hay un borde si usa \ w . Esto coincidiría con " foo " o " barra " ;, así que obviamente también tendrías que reemplazar el espacio en blanco, por si acaso. (Suponiendo que esté reemplazando algo).

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

No creo que esto se pueda hacer con una sola expresión regular. Y los límites pueden o no funcionar, dependiendo de lo que compares.

Coincidiría con 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...

Probé con Regex Coach contra:

x foo y
x bar y
x foobar y

Si selecciono la opción g , de hecho coincide con las tres palabras, porque busca de nuevo después de cada coincidencia.
Si no desea este comportamiento, puede anclar la expresión, por ejemplo, haciendo coincidir solo los límites de las palabras:

\b(foo|bar)\b

Dar más contexto sobre el problema (cómo se ven los datos) podría dar mejores respuestas.

\b(foo)\b|\b(bar)\b

Y use solo el primer grupo de captura .

Usando los límites de palabras, puede obtener una sola palabra ...

me@home ~  
$ echo "Where is my bar of soap?" | egrep "\bfoo\b|\bbar\b"  
Where is my bar of soap?  

me@home ~  
$ echo "What the foo happened here?" | egrep "\bfoo\b|\bbar\b"  
What the foo happened here?  

me@home ~  
$ echo "Boy, that sure is foobar\!" | egrep "\bfoo\b|\bbar\b"  
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top