Подтвердить что ты не робот

Отрицательное регулярное выражение для соответствия строки строки Perl

У меня есть это регулярное выражение:

if($string =~ m/^(Clinton|[^Bush]|Reagan)/i)
  {print "$string\n"};

Я хочу сочетаться с Клинтоном и Рейганом, но не с Бушем.

Не работает.

4b9b3361

Ответ 1

Пример текста:

Клинтон сказал
Буш использовал карандаши
Рейган забыл

Просто опуская матч в Буше:

$ perl -ne 'print if /^(Clinton|Reagan)/' textfile
Clinton said
Reagan forgot

Или, если вы действительно хотите указать:

$ perl -ne 'print if /^(?!Bush)(Clinton|Reagan)/' textfile
Clinton said
Reagan forgot

Ответ 2

Ваше регулярное выражение не работает, потому что [] определяет класс символов, но то, что вы хотите, - это просмотр:

(?=) - Positive look ahead assertion foo(?=bar) matches foo when followed by bar
(?!) - Negative look ahead assertion foo(?!bar) matches foo when not followed by bar
(?<=) - Positive look behind assertion (?<=foo)bar matches bar when preceded by foo
(?<!) - Negative look behind assertion (?<!foo)bar matches bar when NOT preceded by foo
(?>) - Once-only subpatterns (?>\d+)bar Performance enhancing when bar not present
(?(x)) - Conditional subpatterns
(?(3)foo|fu)bar - Matches foo if 3rd subpattern has matched, fu if not
(?#) - Comment (?# Pattern does x y or z)

Итак, попробуйте: (?! bush)

Ответ 3

Ваше регулярное выражение говорит следующее:

/^         - if the line starts with
(          - start a capture group
Clinton|   - "Clinton" 
|          - or
[^Bush]    - Any single character except "B", "u", "s" or "h"
|          - or
Reagan)   - "Reagan". End capture group.
/i         - Make matches case-insensitive 

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

Our president, George Bush
In the news today, pigs can fly
012-3123 33

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

if( ($string =~ m/^(Clinton|Reagan)/i) and
    ($string !~ m/^Bush/i) ) {
   print "$string\n";
}

Как отмечалось в комментариях mirod, вторая проверка совершенно не нужна при использовании каретки (^) для соответствия только началу строк, поскольку линии, начинающиеся с "Клинтона" или "Рейгана", никогда не могут начинаться с "Буша".

Однако это было бы справедливо без кареток.

Ответ 4

Что случилось с использованием двух регулярных выражений (или трех)? Это делает ваши намерения более ясными и может даже улучшить вашу производительность:

if ($string =~ /^(Clinton|Reagan)/i && $string !~ /Bush/i) { ... }

if (($string =~ /^Clinton/i || $string =~ /^Reagan/i)
        && $string !~ /Bush/i) {
    print "$string\n"
}

Ответ 5

Если мое понимание правильное, вы хотите соответствовать любой строке, в которой есть Клинтон и Рейган, в любом порядке, но не в Буше. Как было предложено Stuck, вот версия с прогнозными утверждениями:

#!/usr/bin/perl

use strict;
use warnings;

my $regex = qr/
    (?=.*clinton)  
    (?!.*bush) 
    .*reagan       
    /ix;

while (<DATA>) {
    chomp;
    next unless (/$regex/);
    print $_, "\n";
}


__DATA__
shouldn't match - reagan came first, then clinton, finally bush
first match - first two: reagan and clinton
second match - first two reverse: clinton and reagan
shouldn't match - last two: clinton and bush
shouldn't match - reverse: bush and clinton
shouldn't match - and then came obama, along comes mary
shouldn't match - to clinton with perl

Результаты

first match - first two: reagan and clinton
second match - first two reverse: clinton and reagan

по желанию, он соответствует любой строке, в которой есть Рейган и Клинтон в любом порядке.

Возможно, вам захочется попробовать прочитать, как выглядящие утверждения работают с примерами в http://www252.pair.com/comdog/mastering_perl/Chapters/02.advanced_regular_expressions.html

они очень вкусные:)