Как отрицать конкретное слово в регулярных выражениях?

494

Я знаю, что я могу отрицать группу символов, как в [^bar], но мне нужно регулярное выражение, где отрицание относится к конкретному слову - так что в моем примере, как я отрицаю фактический "bar", а не "any chars in bar"?

Теги:

10 ответов

577
Лучший ответ

Отличный способ сделать это - использовать негативный взгляд:

^(?!.*bar).*$

Конструкция негативного прогноза представляет собой пару круглых скобок с открывающей скобкой, за которой следует вопросительный знак и восклицательный знак. Внутри lookahead [является любым шаблоном регулярного выражения].

  • 9
    Это говорит само за себя (я бы, наверное, начал с (?! Bar) и наращивал). Я не понимаю, почему другие люди делают это так сложно.
  • 32
    К сожалению, это не работает со всеми языками.
Показать ещё 10 комментариев
58

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

Регулярные выражения обычно означают, что вы все равно выполняете скрипты или какую-то низкопроизводительную задачу, поэтому находите решение, которое легко читать, легко понять и легко поддерживать.

  • 3
    Есть много ситуаций, когда вы не контролируете рабочий процесс: вы просто пишете одно регулярное выражение, которое является фильтром.
40

Следующее регулярное выражение будет делать то, что вы хотите (до тех пор, пока поддерживаются отрицательные lookbehinds и lookaheads), правильно подходящие вещи; единственная проблема заключается в том, что он соответствует отдельным символам (т.е. каждое совпадение - это один символ, а не все символы между двумя последовательными "барами" ), что может привести к высоким накладным расходам, если вы работаете с очень длинными строками.

b(?!ar)|(?<!b)a|a(?!r)|(?<!ba)r|[^bar]
  • 6
    Вместо тех многочисленных обновлений, которые вынуждают нас читать неправильные ответы, прежде чем мы дойдем до вашего окончательного ответа, почему бы не переписать ваш ответ, чтобы он был полным, но без несколько запутанных плохих частей? Если кого-то действительно волнует история редактирования, он может использовать встроенные функции этого сайта.
  • 12
    Прошло два с половиной года с тех пор, как я написал этот ответ, но уверен.
Показать ещё 2 комментария
37

Вы можете использовать негативный внешний вид или внешний вид:

^(?!.*?bar).*
^(.(?<!bar))*?$

Или используйте только основы:

^(?:[^b]+|b(?:$|[^a]|a(?:$|[^r])))*$

Все они соответствуют всем, что не содержит bar.

  • 0
    Какие языки не поддерживают (отрицательные) упреждения и / или (отрицательные) упреждения в регулярных выражениях?
  • 5
    Я думаю, что суть в том, что, глядя на ваш паттерн, совсем не ясно, что все, что вы делаете - это отказываетесь от слова «бар».
Показать ещё 6 комментариев
25

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

Учитывая входную строку, сопоставьте все, если только эта строка ввода не является "баром"; например, я хочу совместить "барьер" и "запрет", а также "foo".

Здесь regex я придумал

^(bar.+|(?!bar).*)$

Мой английский перевод регулярного выражения "соответствует строке, если она начинается с" bar ", и у нее есть хотя бы один другой символ, или если строка не начинается с" bar ".

  • 0
    @ReReqest - у вас будет гораздо больше шансов получить ответ на этот вопрос, если вы разместите его как отдельный вопрос. В этом вы можете предоставить ссылку на этот вопрос, если хотите. По существу вопроса - это выглядит хорошо, но я не гуру регулярных выражений
  • 1
    Это был тот, кого я искал. Это действительно соответствует всему, кроме бара.
Показать ещё 1 комментарий
22

Решение:

^(?!.*STRING1|.*STRING2|.*STRING3).*$

xxxxxx ОК

xxxSTRING1xxx KO (желательно ли это)

xxxSTRING2xxx KO (желательно ли это)

xxxSTRING3xxx KO (желательно ли это)

  • 2
    спасибо, это дало мне дополнительную информацию, необходимую для нескольких слов
5

Принятый ответ хорош, но на самом деле является обходным процессом из-за отсутствия простого оператора выражения выражения выражений выражения в регулярных выражениях. Вот почему grep --invert-match завершает работу. Таким образом, в * nixes вы можете выполнить желаемый результат с помощью труб и второго регулярного выражения.

grep 'something I want' | grep --invert-match 'but not these ones'

Все еще обходной путь, но, возможно, легче запомнить.

2

Я надеюсь дополнить ответ

Как уточнил Крис Regex Tutorial - лучший ресурс для обучения регулярному выражению.

Однако он действительно потреблял время для чтения.

Я делаю чит-коды для удобства мнемоники.
[], (), {} ведущие к каждому классу, который легко вспомнить.

Regex =
{'single_character': ['[]', '.', {'negate':'^'}],
 'capturing_group' : ['()', '|', '\\', 'backreferences and named group'],
 'repetition'      : ['{}', '*', '+', '?', 'greedy v.s. lazy'],
 'anchor'          : ['^', '\b', '$'],
 'non_printable'   : ['\n', '\t', '\r', '\f', '\v'],
 'shorthand'       : ['\d', '\w', '\s'],
 }
1

У меня был список имен файлов, и я хотел исключить некоторые из них с таким поведением (Ruby):

files = [
  'mydir/states.rb',      # don't match these
  'countries.rb',
  'mydir/states_bkp.rb',  # match these
  'mydir/city_states.rb' 
]
excluded = ['states', 'countries']

# set my_rgx here

result = WankyAPI.filter(files, my_rgx)  # I didn't write WankyAPI...
assert result == ['mydir/city_states.rb', 'mydir/states_bkp.rb']

Здесь мое решение:

excluded_rgx = excluded.map{|e| e+'\.'}.join('|')
my_rgx = /(^|\/)((?!#{excluded_rgx})[^\.\/]*)\.rb$/

Мои предположения для этого приложения:

  • Строка, которая должна быть исключена, находится в начале ввода или сразу же после косой черты.
  • Допустимые строки заканчиваются на .rb.
  • Допустимые имена файлов не имеют символа . перед .rb.
1

Просто подумал о чем-то другом, что можно было бы сделать. Это очень отличается от моего первого ответа, поскольку он не использует регулярные выражения, поэтому я решил сделать второй ответ.

Используйте свой язык выбора split() эквивалент метода в строке со словом, чтобы отрицать как аргумент для разделения. Пример использования Python:

>>> text = 'barbarasdbarbar 1234egb ar bar32 sdfbaraadf'
>>> text.split('bar')
['', '', 'asd', '', ' 1234egb ar ', '32 sdf', 'aadf']

Хорошая вещь в этом, по крайней мере, в Python (я не помню, была ли функциональность такой же, например, Visual Basic или Java), заключается в том, что она позволяет вам опознать, когда "bar" был повторен в строке из-за того, что пустые строки между "bar" s включены в список результатов (хотя пустая строка в начале происходит из-за наличия "бара" в начале строки). Если вы этого не хотите, вы можете просто удалить пустые строки из списка.

  • 0
    Вопрос специально задает о регулярных выражениях ...
  • 2
    @Ajk_P да, но ответы такого типа могут помочь оператору мыслить нестандартно, они могли быть зациклены на регулярных выражениях, не понимая, что их можно решить без них.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню