Исключительное или в регулярном выражении

Нужна помощь по регулярным выражениям. Я хотел бы создать выражение, которое соответствует строке с «фу» ИЛИ «бар», но не одновременно с «фу» И «бар»

Если я сделаю что-то вроде ...

/((foo)|(bar))/

Это будет соответствовать "foobar". Не то, что я ищу. Итак, как я могу сопоставить регулярное выражение только при наличии одного или другого термина?

Спасибо!

Подходит ли foofoobar, потому что он содержит «foo» и «foobar»? Как насчет "фоонбара"? Не могли бы вы привести примеры совпадений и несовпадений?

Thomas Owens 29.10.2008 18:11

Соответствует: "foo", "bar" не соответствует: "foofoo" "barfoo" "foobarfoo" "barbar" "barfoofoo"

SocialCensus 29.10.2008 18:29

Если вы не хотите, чтобы "foofoo" совпадало, то вы на самом деле не говорите об эксклюзивном или.

cjm 29.10.2008 20:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
31
3
45 681
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Ответ принят как подходящий

Вы можете сделать это с помощью одного регулярного выражения, но я предлагаю для удобства чтения сделать что-то вроде ...

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

В самом деле, я почти уверен, что поместил бы логику XOR в сам код, а не в регулярное выражение.

Pistos 29.10.2008 19:18

Или даже лучше, / foo / xor / bar /, если в вашем языке есть оператор XOR. (Perl делает.)

cjm 29.10.2008 20:47

@Ralf Это не одно выражение, это два выражения, соединенных логическим оператором ИЛИ.

Ed Guiness 23.02.2018 14:02

Я бы использовал что-то вроде этого. Он просто проверяет наличие места вокруг слов, но вы можете использовать \b или \B для проверки границы, если вы используете \w. Это будет соответствовать "foo" или "bar", так что, очевидно, вам придется заменить пробел, на всякий случай. (Предполагая, что вы что-нибудь заменяете.)

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

Я не думаю, что это можно сделать с помощью одного регулярного выражения. И границы могут работать или не работать в зависимости от того, с чем вы сопоставляете.

Я бы сопоставлял каждое регулярное выражение отдельно и выполнял XOR по результатам.

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

Я пробовал с Regex Coach против:

x foo y
x bar y
x foobar y

Если я проверю опцию g, она действительно соответствует всем трем словам, потому что поиск выполняется снова после каждого совпадения. Если вам не нужно такое поведение, вы можете привязать выражение, например, сопоставить только по границам слова:

\b(foo|bar)\b

Более подробный контекст проблемы (как выглядят данные) может дать лучшие ответы.

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

И используйте только первый группа захвата.

Возможно, вы захотите рассмотреть? условный тест.

(?(?=regex)then|else)

Условные выражения регулярных выражений

Если ваш язык регулярных выражений поддерживает это, используйте отрицательный взгляд:

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

Это будет соответствовать «foo» или «bar», которым непосредственно не предшествуют или не следует «foo» или «bar», что, я думаю, именно то, что вы хотели.

Из вашего вопроса или примеров неясно, может ли строка, которую вы пытаетесь сопоставить, содержать другие токены: «foocuzbar». Если так, то этот шаблон не сработает.

Вот результаты ваших тестовых случаев («истина» означает, что шаблон был найден во входных данных):

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

Используя границы слов, вы можете получить одно слово ...

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"  

Вы не указали поведение в отношении содержимого, кроме "foo" и "bar" или повторений одного в отсутствие другого. например, должно ли совпадать "фуd" или "варварian"?

Предполагая, что вы хотите сопоставить строки, которые содержат только один экземпляр либо «foo», либо «bar», но не оба и не несколько экземпляров одного и того же, без учета чего-либо еще в строке (т. Е. «Food» соответствует и "варвар" не соответствует), то вы можете использовать регулярное выражение, которое возвращает количество найденных совпадений и считает его успешным только в том случае, если найдено ровно одно совпадение. например, в Perl:

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

Если разрешено многократное повторение одной и той же цели (например, «варварские» совпадения), то этот же общий подход можно использовать, затем просматривая список совпадений, чтобы увидеть, являются ли совпадения все повторениями одного и того же текста или другой вариант тоже присутствует.

Если вам нужен настоящий эксклюзив или, я бы просто сделал это в коде, а не в регулярном выражении. В Perl:

/foo/ xor /bar/

Но ваш комментарий:

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

означает, что вы на самом деле не ищете эксклюзивное или. Вы действительно имеете в виду "/foo|bar/ соответствует ровно один раз?"

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

my $ok = ($matches == 1)

Вот что я использую:

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

См .: http://www.regular-expressions.info/quickstart.html при повторении

Намного более элегантное решение, чем принятый ответ, особенно когда у вас более двух случаев ..

Manuel Arwed Schmidt 28.09.2015 17:48

Почему вы добавили {1}, что это значит?

oriadam 05.06.2016 13:35

Это неверно, это означает, что foo или bar должны совпадать только один раз.

Karl 05.07.2016 04:56

Я согласен с @Karl, это не XOR. Он только проверяет, является ли вся строка "foo" или "bar"

ecdani 29.08.2019 10:59

Это будет принимать 'foo' и 'bar', но не 'foobar' и не 'blafoo', а не 'blabar':

/^(foo|bar)$/

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

Это будет принимать 'foo', 'bar' и 'foo bar' и 'bar-foo', но не 'foobar' и не 'blafoo', и не 'blabar':

/\b(foo|bar)\b/

\b = mark word boundry

Я знаю, что это поздняя запись, но просто чтобы помочь другим, кто может искать:

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

Другие вопросы по теме