Регулярные выражения и отрицание всей группы символов

Я пытаюсь сделать что-то, что, по моему мнению, должно быть достаточно очевидным для меня, но это не так. Я пытаюсь сопоставить строку, которая не содержит определенную последовательность символов. Я пытался использовать [^ab], [^(ab)]и т. д. для сопоставления строк, не содержащих "a" или "b", или только "a" или "b" или "ba", но не совпадающих с "ab". Примеры, которые я привел, не будут соответствовать 'ab', это правда, но они также не будут соответствовать 'a', и я нуждаюсь в них. Есть ли какой-нибудь простой способ сделать это?

9 ответов

Решение

Используйте негативную перспективу:

^(?!.*ab).*$

ОБНОВЛЕНИЕ: В комментариях ниже я заявил, что этот подход медленнее, чем тот, который приведен в ответе Питера. С тех пор я провел несколько тестов и обнаружил, что это действительно немного быстрее. Однако причина, по которой этот метод предпочитается другим, заключается не в скорости, а в простоте.

Другой метод, описанный здесь как закаленный жадный токен, подходит для более сложных задач, таких как сопоставление текста с разделителями, где разделители состоят из нескольких символов (например, HTML, как прокомментировал Люк ниже). Для проблемы, описанной в вопросе, это перебор.

Для тех, кто заинтересован, я протестировал большой кусок текста Lorem Ipsum, подсчитав количество строк, в которых нет слова "quo". Вот те регулярные выражения, которые я использовал:

(?m)^(?!.*\bquo\b).+$

(?m)^(?:(?!\bquo\b).)+$

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

Использование класса символов, такого как [^ab] будет соответствовать одному символу, который не входит в набор символов. (С ^ являясь отрицательной частью).

Соответствовать строке, которая не содержит многосимвольную последовательность ab, вы хотите использовать негативный взгляд:

^(?:(?!ab).)+$


И приведенное выше выражение в режиме комментариев регулярного выражения:

(?x)    # enable regex comment mode
^       # match start of line/string
(?:     # begin non-capturing group
  (?!   # begin negative lookahead
    ab  # literal text sequence ab
  )     # end negative lookahead
  .     # any single character
)       # end non-capturing group
+       # repeat previous match one or more times
$       # match end of line/string

Да, это называется негативным взглядом. Это идет так - (?!regex here), Так abc(?!def) будет соответствовать abc, а не def. Так что это будет соответствовать abce, abc, abck и т. Д.

Точно так же есть положительный взгляд - (?=regex here), Так abc(?=def) будет соответствовать abc и def.

Есть также отрицательный и положительный взгляд сзади - (?<!regex here) а также (?<=regex here) соответственно

Стоит отметить, что отрицательный прогноз - нулевая ширина. То есть он не считается занятым.

Так может выглядеть a(?=b)c будет соответствовать "abc", но не будет. Он будет соответствовать 'a', тогда положительный взгляд с 'b', но он не будет двигаться вперед в строке. Затем он попытается сопоставить "c" с "b", что не сработает. так же ^a(?=b)b$ будет соответствовать 'ab', а не 'abb', потому что обходные пути имеют нулевую ширину (в большинстве реализаций регулярных выражений).

Больше информации на этой странице

Использование регулярного выражения, как вы описали, является простым способом (насколько я знаю). Если вы хотите диапазон, вы можете использовать [^af].

Самый простой способ - полностью вывести отрицание из регулярного выражения:

if (!userName.matches("^([Ss]ys)?admin$")) { ... }

abc(?!def) будет соответствовать abc, за которым не следует def. Так что это будет соответствовать abce, abc, abck и т. Д. Что, если я не хочу ни def, ни xyz, это будет abc(?!(Def) (xyz))???

У меня был тот же вопрос, и я нашел решение:

abc(?:(?!def))(?:(?!xyz))

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

Регулярное выражение [^(ab)] будет соответствовать, например, "ab ab ab ab", но не "ab", потому что оно будет соответствовать строке "a" или "b".

Какой у вас язык / сценарий? Можете ли вы вычесть результаты из исходного набора, и просто сопоставить аб?

Если вы используете GNU grep и анализируете ввод, используйте флаг '-v', чтобы инвертировать результаты, возвращая все несоответствия. У других инструментов регулярных выражений также есть функция return nonmatch.

Если я правильно понимаю, вы хотите все, кроме тех элементов, которые где-либо содержат "ab".

Просто найдите "ab" в строке и затем отрицайте результат:

!/ab/.test("bamboo"); // true
!/ab/.test("baobab"); // false

Это кажется проще и должно быть быстрее тоже.

В этом случае я мог бы просто полностью избежать регулярных выражений и пойти с чем-то вроде:

if (StringToTest.IndexOf("ab") < 0)
  //do stuff

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

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