Регулярные выражения и отрицание всей группы символов
Я пытаюсь сделать что-то, что, по моему мнению, должно быть достаточно очевидным для меня, но это не так. Я пытаюсь сопоставить строку, которая не содержит определенную последовательность символов. Я пытался использовать [^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", просто проверить, содержит ли строка эту строку, без использования регулярных выражений.