Эксклюзивно или в Регулярном выражении

StackOverflow https://stackoverflow.com/questions/247167

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

/((foo)|(bar))/

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

Спасибо!

Это было полезно?

Решение

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

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

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

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

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

Видишь: http://www.regular-expressions.info/quickstart.html при повторении

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

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

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

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

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

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

Это займет '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

Вы не указали поведение в отношении контента, отличного от "foo" и "bar", или повторения одного из них в отсутствие другого.например, следует "фуd" или "варвариэн" совпадает?

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

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

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

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

(?(?=regex)then|else)

Условные обозначения регулярного выражения

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

/foo/ xor /bar/

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

Матчи:"foo", "bar" не соответствует:"фуфу", "барфу", "фубарфу", "варвар" "барфу-фуфу"

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

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

my $ok = ($matches == 1)

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

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

Я бы использовал что-то вроде этого.Он просто проверяет наличие пробелов вокруг слов, но вы могли бы использовать \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

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

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

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"  
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top