Как я могу перехватить все непустые последовательности букв, кроме кошки, собаки, рыбы, используя регулярное выражение? - PullRequest
1 голос
/ 26 сентября 2010

Пожалуйста, объясните, почему выражение имеет смысл, если оно сложное.

Ответы [ 3 ]

1 голос
/ 26 сентября 2010

Давайте рассмотрим, как мы можем создать шаблон, который исключает определенные фразы.

Мы начнем с простого .*, который соответствует любому символу (используя точка ),ноль или более раз ( звезда ).Этот шаблон будет соответствовать любой строке, включая пустую строку 1 .

Однако, поскольку существуют определенные фразы, которые мы не хотим найти, мы можем попытатьсяиспользуйте отрицательный lookaround , чтобы помешать ему соответствовать тому, что мы не хотим.Обратный взгляд - это утверждение нулевой ширины , что означает, что движку регулярных выражений необходимо удовлетворить утверждение, чтобы совпадение было, но утверждение не использует никаких символов (или, другими словами, оно не 'продвинуть позицию в строке).В этом конкретном случае мы будем использовать lookahead , который сообщает механизму регулярных выражений, чтобы он смотрел вперед текущей позиции, чтобы соответствовать утверждению (также есть lookbehinds , которые, естественно, смотрят за текущую позицию).Поэтому мы попробуем (?!cat|dog|fish).*.

Когда мы попробуем этот шаблон против catdogfish, он будет соответствовать atdogfish!Что тут происходит?Давайте посмотрим, что происходит, когда движок пытается использовать наш шаблон на catdogfish.

Двигатель работает слева направо, начиная с первого символа в нашей строке.С первой попытки предвестник утверждает, что следующие символы из этой точки не являются cat, dog или fish, но, поскольку они на самом деле cat, с этого момента двигатель не может соответствовать, и переходит кперед вторым персонажем.Здесь утверждение успешно, потому что следующие следующие символы не удовлетворяют утверждению (atf не соответствует cat или dog и atfi не соответствует fish).Теперь, когда утверждение выполнено успешно, движок может соответствовать .*, и поскольку по умолчанию регулярные выражения жадные (что означает, что они будут захватывать как можно большую часть вашей строки),точка-звезда будет поглощать оставшуюся часть строки.

Возможно, вы удивляетесь, почему обход не проверяется снова после успешного выполнения первого утверждения.Это потому, что точка-звезда берется как один единственный токен, с внешним видом, работающим над ним в целом.Давайте изменим это так, чтобы внешний вид утверждался один раз за повторение: (?:(?!cat|dog|fish).)*.

(?:…) называется не захватывающей группой .Как правило, все элементы в регулярных выражениях сгруппированы в круглые скобки, но эти круглые скобки захватывают , что означает, что содержимое сохраняется в обратной ссылке (или подстроке).Так как здесь нам не нужно подсовпадение, мы можем использовать группу без захвата, которая работает так же, как обычная группа, но без накладных расходов на отслеживание обратной ссылки.

Когда мы запускаем нашу новуюс шаблоном против catdogfish, теперь мы получаем три матча 2 : at, og и ish!Давайте посмотрим, что происходит на этот раз внутри движка регулярных выражений.

И снова двигатель запускается перед первым символом.Он входит в группу, которая будет повторяться ((?!cat|dog|fish).) и видит, что утверждение не выполнено, поэтому перемещается на следующую позицию (a).Утверждение успешно выполнено, и двигатель движется вперед до t.Снова утверждение успешно, и двигатель снова движется вперед.В этот момент утверждение не выполняется (потому что следующие три символа dog), и механизм возвращает at как совпадение, потому что это самая большая строка (пока, и механизм работает слева направо),это соответствует шаблону.

Далее, даже если у нас уже есть совпадение, двигатель продолжит работу.Он переместится вперед к следующему символу (o) и снова выберет два символа, соответствующих шаблону (og).Наконец, то же самое произойдет для ish в конце строки.Как только двигатель достигает конца строки, ему больше нечего делать, и он возвращает три найденных совпадения.

Так что этот шаблон все еще не совершенен, потому что он будет соответствовать частям строки, содержащей наши запрещенные фразы.Чтобы предотвратить это, нам нужно ввести якоря в наш шаблон: ^(?:(?!cat|dog|fish).)*$

Якоря также являются утверждениями нулевой ширины, которые утверждают, что положение, в котором находится двигатель, должно бытьконкретное место в строке.В нашем случае ^ соответствует началу строки, а $ соответствует концу строки.Теперь, когда мы сопоставляем наш шаблон с catdogfish, ни одно из этих небольших совпадений больше не может быть найдено, потому что ни одно из них не соответствует позициям привязки.

Таким образом, окончательное выражение будет ^(?:(?!cat|dog|fish).)*$.


1 Однако по умолчанию точка не соответствует символам новой строки, если только не включен модификатор *1000* на регулярное выражение. 2 Здесь я делаю предположение, что шаблон работает в «глобальном» режиме, что позволяет ему соответствовать столько раз, сколько возможно.Без глобального режима шаблон будет возвращать только первое совпадение, at.

1 голос
/ 26 сентября 2010

Если вы на самом деле используете grep, вы можете использовать опцию -v, чтобы выбрать только строки, которые не соответствуют:

grep -v \(cat\|dog\|fish\|^$\)

Шаблон будет выбирать пустые строки и строки, содержащие "cat "," dog "и" fish ".

Хорошо, вы не используете grep.Согласно http://www.regular -expressions.info / refadv.html , если ваш движок регулярных выражений поддерживает это, вы хотите ?!:

`(?! Regex)` отрицательный по нулевой ширинесмотреть вперед.Идентичен положительному предпросмотру, за исключением того, что общее совпадение будет успешным только в том случае, если регулярное выражение внутри заглядывания не соответствует.`t (?! s)` соответствует первому `t` на улицах.
0 голосов
/ 27 сентября 2010

Обычно лучше оставить отрицание кода «вокруг» регулярного выражения - такого как ключ -v в grep или! ~ В perl.Есть ли конкретная проблема, которую вы пытаетесь решить, или это просто упражнение?

...