Exclusive ou en Expression régulière

Je cherche un peu d'aide regex. Je souhaiterais créer une expression qui correspond à une chaîne avec " foo" OU " bar", mais pas les deux " foo "et" bar"

Si je fais quelque chose comme...

/((foo)|(bar))/

Il va correspondre "blabla". Pas ce que je cherche. Alors, comment puis-je faire correspondre regex seulement quand un terme ou l'autre est présent?

Merci!

23
demandé sur agc 2008-10-29 18:07:31

13 réponses

Vous pouvez le faire avec un seul regex, mais je vous suggère pour des raisons de lisibilité de faire quelque chose comme...

(/foo/ and not /bar/) || (/bar/ and not /foo/)
8
répondu Ed Guiness 2008-10-29 15:11:08

c'est Ce que j'utilise:

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

voir:http://www.regular-expressions.info/quickstart.html sous répétition

26
répondu Tristan 2011-12-02 19:57:57

si votre langage regex le supporte, utilisez négatif lookaround:

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

"foo" ou "bar" qui n'est pas immédiatement précédée ou suivie par "foo", "bar", qui, je pense, est ce que tu voulais.

il n'est pas clair à partir de votre question ou de vos exemples si la chaîne que vous essayez de faire correspondre peut contenir d'autres tokens: "foocuzbar". Si c'est le cas, ce schéma ne fonctionnera pas.

Voici les résultats de vos cas de test ("vrai" signifie le modèle a été trouvé dans l'entrée):

foo: true
bar: true
foofoo: false
barfoo: false
foobarfoo: false
barbar: false
barfoofoo: false
15
répondu erickson 2008-10-29 16:32:06

cela prendra 'foo' et ' bar 'mais pas' foobar 'et pas' blafoo 'et pas 'blabar':

/^(foo|bar)$/

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

Cela va prendre " foo " et " bar " et "foo bar" et "bar-foo', mais pas 'foobar' et pas 'blafoo" et non pas "blabar':

/\b(foo|bar)\b/

\b = mark word boundry
6
répondu oriadam 2018-01-29 15:19:37

vous pourriez considérer le ? test conditionnel.

(?(?=regex)then|else)

Conditions D'Expression Régulière

2
répondu Josh 2008-10-29 15:56:11

Vous n'avez pas spécifié de comportement concernant le contenu autre que "foo" et "bar" ou les répétitions de l'un en l'absence de l'autre. par exemple, Devrait " food" ou " barbar ian" match?

en supposant que vous voulez associer des chaînes qui ne contiennent qu'une seule instance de "foo" ou "bar", mais pas les deux et pas plusieurs instances du même, sans tenir compte de quoi que ce soit d'autre dans la chaîne (i.e., "food" correspond et "barbarian" ne correspond pas), alors vous pourrait utiliser un regex qui renvoie le nombre de correspondances trouvées et ne le considère comme réussi que si exactement une correspondance est trouvée. par exemple, en Perl:

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

si plusieurs répétitions de la même cible sont permises (c.-à-d. des correspondances "barbares"), alors cette même approche générale pourrait être utilisée en parcourant la liste des correspondances pour voir si les correspondances sont toutes des répétitions du même texte ou si l'autre option est également présente.

2
répondu Dave Sherohman 2008-10-29 16:59:24

si vous voulez une vraie exclusivité or, je le ferais en code plutôt que dans le regex. En Perl:

/foo/ xor /bar/

Mais votre commentaire:

Correspond à: "foo", "bar" nonmatches: "foofoo "" barfoo "" fobarfoo "" barbar" "barfoofoo"

indique que vous n'êtes pas vraiment à la recherche de l'exclusivité or. Vous avez réellement dire "N' /foo|bar/ correspondre exactement une fois?"

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

my $ok = ($matches == 1)
1
répondu cjm 2008-10-29 17:59:56

je sais que c'est un retard, mais juste pour aider les autres qui peuvent être à la recherche de:

(/b(?:(?:(?!foo)bar)|(?:(?!bar)foo))/b)
1
répondu qmckinsey 2015-07-06 03:39:04

j'utiliserais quelque chose comme ça. Il vérifie juste pour l'espace autour des mots, mais vous pouvez utiliser le \b ou \B à vérifier la frontière si vous utilisez \w. Cela correspondrait à " foo " ou "bar", donc évidemment vous devriez remplacer l'espace blanc aussi, juste au cas. (En supposant que tu remplaces n'importe quoi.)

/\s((foo)|(bar))\s/
0
répondu Robert K 2008-10-29 15:15:55

je ne pense pas que cela peut être fait avec une seule expression régulière. Et les limites peuvent ou ne peuvent pas fonctionner selon ce contre quoi vous vous comparez.

Je comparerais avec chaque regex séparément, et je ferais un XOR sur les résultats.

foo = re.search("foo", str) != None
bar = re.search("bar", str) != None
if foo ^ bar:
    # do someting...
0
répondu Can Berk Güder 2008-10-29 15:23:32

j'ai essayé avec la Regex Entraîneur contre:

x foo y
x bar y
x foobar y

Si je coche g option, en effet elle correspond aux trois mots, car elle cherche à nouveau après chaque correspondance.

Si vous ne voulez pas ce comportement, vous pouvez ancrer l'expression, par exemple en ne faisant correspondre que sur les limites de mots:

\b(foo|bar)\b

donner plus de contexte sur le problème (à quoi ressemblent les données) pourrait donner de meilleures réponses.

0
répondu PhiLho 2008-10-29 15:27:36
\b(foo)\b|\b(bar)\b

Et utiliser uniquement le premier groupe.

0
répondu Andrew Cowenhoven 2008-10-29 15:41:23

en utilisant le mot limites, vous pouvez obtenir le mot simple...

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"  
0
répondu bill_the_loser 2008-10-29 16:44:27