Как я могу сопоставить строки, которые не соответствуют определенному шаблону в Perl?

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

$text = "ab ac ad";
$text =~ s/[^c]*//g; # Match anything, except c.

$text is now "c".

Я не знаю, как "исключать" строки вместо символов. Как мне "соответствовать что угодно, кроме" ac "? Пробовал [^ (ac)] и [^" ac"] без успеха.

Возможно ли вообще?

Ответ 1

Далее решается вопрос, понятый во втором смысле, описанном в комментарии Барта К.:

>> $text='ab ac ad';
>> $text =~ s/(ac)|./\1/g;
>> print $text;
ac

Кроме того, 'abacadac''acac'

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

Ответ 2

Если вы просто хотите проверить, не содержит ли строка "ac", просто используйте отрицание.

$text = "ab ac ad";

print "ac not found" if $text !~ /ac/;

или

print "ac not found" unless $text =~ /ac/;

Ответ 3

$text =~ s/[^c]*//g; // Match anything, except c.

@ssn, Несколько комментариев по вашему вопросу:

  • "//" не является комментарием в Perl. Только "#" есть.
  • "[^ c] *" - нет необходимости в "*". "[^ c]" означает класс персонажа, состоящий из всех кроме буквы "c". Затем вы используете модификатор /g, что все такие вхождения в текст будут (в вашем примере, с ничего). "Ноль или больше" ( "*" ) поэтому модификатор является избыточным.

Как мне "соответствовать", кроме 'ac'?? Пробовал [^ (ac)] и [^ "ac" ] без успеха.

Прочитайте документацию по классам символов (см. "perldoc perlre" в командной строке или в Интернете по адресу http://perldoc.perl.org/perlre.html) - вы 'Посмотрим, что это означает, что для списка символов в квадратных скобках RE будет "соответствовать любому символу из списка". Значение порядка не имеет значения, и нет "строк", только список символов. "()", а двойные кавычки также не имеют особого значения в квадратных скобках.

Теперь я не совсем уверен, почему вы говорите о совпадении, но затем приводите пример замещения. Но чтобы убедиться, что строка не соответствует подстроке "ac" , вам просто нужно отменить соответствие:

use strict; use warnings;
my $text = "ab ac ad";
if ($text !~ m/ac/) {
   print "Yey the text doesn't match 'ac'!\n"; # this shouldn't be printed
}

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

$text =~ s/ac//g;

Если вы хотите обратный - удалить весь текст, кроме всех вхождений подстроки, я бы предложил что-то вроде:

use strict; use warnings;
my $text = "ab ac ad ac ae";
my $sub_str = "ac";
my @captured = $text =~ m/($sub_str)/g;
my $num = scalar @captured;
print (($sub_str x $num) . "\n");

В основном это подсчет количества раз, когда подстрока появляется в тексте и печатает подстроку, количество раз использующее оператор "x". Не очень элегантный, я уверен, что Perl-гуру может придумать что-то лучшее.


@ennuikiller

my $text = "ab ac ad";
$text !~ s/(ac)//g; # Match anything, except ac.

Это неверно, поскольку он генерирует предупреждение ( "Бесполезное использование отрицательной привязки шаблона (! ~) в контексте void" ) в разделе "Использовать предупреждения" и ничего не делает, кроме как удалить все подстроки "ac" из текста, который можно было бы проще написать, как я писал выше:

$text =~ s/ac//g;

Ответ 4

Обновление:. В комментарии к вашему вопросу вы упомянули, что хотите очистить разметку wiki и удалить сбалансированные последовательности {{... }}. Раздел 6 из Perl FAQ охватывает это: Могу ли я использовать регулярные выражения Perl для соответствия сбалансированному тексту?

Рассмотрим следующую программу:

#! /usr/bin/perl

use warnings;
use strict;

use Text::Balanced qw/ extract_tagged /;

# for demo only
*ARGV = *DATA;

while (<>) {
  if (s/^(.+?)(?=\{\{)//) {
    print $1;
    my(undef,$after) = extract_tagged $_, "{{" => "}}";

    if (defined $after) {
      $_ = $after;
      redo;
    }
  }

  print;
}

__DATA__
Lorem ipsum dolor sit amet, consectetur
adipiscing elit. {{delete me}} Sed quis
nulla ut dolor {{me too}} fringilla
mollis {{ quis {{ ac }} erat.

Его вывод:

Lorem ipsum dolor sit amet, consectetur
adipiscing elit.  Sed quis
nulla ut dolor  fringilla
mollis {{ quis  erat.

В вашем конкретном примере вы можете использовать

$text =~ s/[^ac]|a(?!c)|(?<!a)c//g;

То есть, удалите только a или c, если они не являются частью последовательности ac.

В общем, это сложно сделать с регулярным выражением.

Предположим, вы не хотите foo, за которым следуют необязательные пробелы, а затем bar в $str. Часто это проще и проще проверять отдельно. Например:

die "invalid string ($str)"
  if $str =~ /^.*foo\s*bar/;

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

my $nofoo = qr/
  (      [^f] |
    f  (?! o) |
    fo (?! o  \s* bar)
  )*
/x;

my $pattern = qr/^ $nofoo bar /x;

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

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

Если вас интересуют теоретические подробности, см. Введение в формальные языки и автоматы Питера Линца.

Ответ 5

вы можете использовать index()

$text = "ab ac ad";
print "ac not found" if ( index($text,"ac") == -1 );

Ответ 6

Вы можете легко изменить это регулярное выражение для своей цели.

use Test::More 0.88;

#Match any whole text that does not contain a string
my $re=qr/^(?:(?!ac).)*$/;
my $str='ab ac ad';

ok(!$str=~$re);

$str='ab af ad';
ok($str=~$re);

done_testing();