Question

Vous recherchez un peu d’aide regex. J'aimerais concevoir 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))/

Cela correspond à " foobar ". Pas ce que je cherche. Alors, comment puis-je faire correspondre une expression rationnelle uniquement lorsqu'un terme ou l'autre est présent?

Merci!

Était-ce utile?

La solution

Vous pouvez le faire avec une seule expression rationnelle, mais je suggère, par souci de lisibilité, de faire quelque chose comme ...

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

Autres conseils

Voici ce que j'utilise:

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

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

Si votre langue regex le prend en charge, utilisez la recherche négative :

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

Cela correspondra à " foo " ou "bar" qui n'est pas immédiatement précédé ou suivi de " foo " ou "bar", ce qui, je pense, est ce que vous vouliez.

Votre question ou vos exemples ne permettent pas de savoir si la chaîne que vous essayez de faire correspondre peut contenir d'autres jetons: "foocuzbar". Si tel est le cas, ce modèle ne fonctionnera pas.

Voici les résultats de vos scénarios de test ("vrai" signifie que le motif a été trouvé dans l'entrée):

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

Cela prendra "foo" et "bar" mais pas "foobar" et non "blafoo" et non "blabar":

/^(foo|bar)$/

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

Cela prendra "foo" et "bar" et "foo bar" et "bar-foo" mais pas "foobar" et non "blafoo" et non "blabar":

/\b(foo|bar)\b/

\b = mark word boundry

Vous n'avez pas indiqué de comportement concernant le contenu autre que "foo". et " bar " ou des répétitions de l'un en l'absence de l'autre. Par exemple, faut-il foo d ou " barbar ian". match?

En supposant que vous souhaitiez faire correspondre des chaînes qui ne contiennent qu'une seule instance de "foo". ou "bar", mais pas les deux et pas plusieurs occurrences du même, sans tenir compte de quoi que ce soit d'autre dans la chaîne (c'est-à-dire, "nourriture" correspond et "barbare" ne correspond pas), alors vous pouvez utiliser une expression régulière qui renvoie le nombre de correspondances trouvées et ne la considère comme réussie que si une seule 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 autorisées (c'est-à-dire, des correspondances "barbares"), alors cette même approche générale pourrait être utilisée en parcourant ensuite 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.

Vous pourriez vouloir considérer le? test conditionnel.

(?(?=regex)then|else)

Conditions des expressions régulières

Si vous voulez un vrai ou exclusif, je le ferais en code plutôt qu'en regex. En Perl:

/foo/ xor /bar/

Mais votre commentaire:

  

correspondances: "foo", "bar" non correspondances:   " foofoo " " barfoo " "foobarfoo" "barbar"   "barfoofoo"

indique que vous ne recherchez pas vraiment l'exclusivité ou. Vous voulez dire " / foo | bar / correspond-il exactement une fois?"

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

my $ok = ($matches == 1)

Je sais que c'est une entrée tardive, mais juste pour aider les autres qui pourraient être à la recherche:

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

Je voudrais utiliser quelque chose comme ça. Il vérifie simplement l'espace entre les mots, mais vous pouvez utiliser le \ b ou le \ B pour rechercher une bordure si vous utilisez \ w . . Cela correspondrait à " foo " ou " il est donc évident que vous devez également remplacer les espaces, au cas où. (En supposant que vous remplaciez quoi que ce soit.)

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

Je ne pense pas que cela puisse être fait avec une seule expression régulière. Et les limites peuvent ou ne peuvent pas fonctionner en fonction de ce que vous comparez.

Je mettrais en correspondance chaque regex séparément et ferais un XOR sur les résultats.

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

J'ai essayé avec Regex Coach contre:

x foo y
x bar y
x foobar y

Si je vérifie l'option g , elle correspond effectivement aux trois mots, car elle effectue une nouvelle recherche après chaque correspondance.
Si vous ne souhaitez pas ce comportement, vous pouvez ancrer l'expression, par exemple en ne faisant correspondre que les limites de mot:

\b(foo|bar)\b

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

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

Et utilisez uniquement le premier groupe de capture .

En utilisant les limites de mots, vous pouvez obtenir le mot unique ...

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"  
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top