Нужна помощь по регулярным выражениям. Я хотел бы создать выражение, которое соответствует строке с «фу» ИЛИ «бар», но не одновременно с «фу» И «бар»
Если я сделаю что-то вроде ...
/((foo)|(bar))/
Это будет соответствовать "foobar". Не то, что я ищу. Итак, как я могу сопоставить регулярное выражение только при наличии одного или другого термина?
Спасибо!
Соответствует: "foo", "bar" не соответствует: "foofoo" "barfoo" "foobarfoo" "barbar" "barfoofoo"
Если вы не хотите, чтобы "foofoo" совпадало, то вы на самом деле не говорите об эксклюзивном или.





Вы можете сделать это с помощью одного регулярного выражения, но я предлагаю для удобства чтения сделать что-то вроде ...
(/foo/ and not /bar/) || (/bar/ and not /foo/)
В самом деле, я почти уверен, что поместил бы логику XOR в сам код, а не в регулярное выражение.
Или даже лучше, / foo / xor / bar /, если в вашем языке есть оператор XOR. (Perl делает.)
@Ralf Это не одно выражение, это два выражения, соединенных логическим оператором ИЛИ.
Я бы использовал что-то вроде этого. Он просто проверяет наличие места вокруг слов, но вы можете использовать \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 при повторении
Намного более элегантное решение, чем принятый ответ, особенно когда у вас более двух случаев ..
Почему вы добавили {1}, что это значит?
Это неверно, это означает, что foo или bar должны совпадать только один раз.
Я согласен с @Karl, это не XOR. Он только проверяет, является ли вся строка "foo" или "bar"
Это будет принимать '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)
Подходит ли foofoobar, потому что он содержит «foo» и «foobar»? Как насчет "фоонбара"? Не могли бы вы привести примеры совпадений и несовпадений?