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

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

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

$text is now "c".

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

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

Ответы [ 6 ]

5 голосов
/ 21 января 2010

Следующее решает вопрос, понимаемый во втором смысле, описанном в комментарии Барт К.:

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

Также 'abacadac' -> 'acac'

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

2 голосов
/ 21 января 2010

Обновление: В комментарии к вашему вопросу вы упомянули, что хотите очистить вики-разметку и удалить сбалансированные последовательности {{ ... }}. Раздел 6 FAQ по Perl охватывает это: Можно ли использовать регулярные выражения 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;

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

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

Если вас интересуют теоретические детали, см. Введение в формальные языки и автоматы , автор Peter Linz.

2 голосов
/ 21 января 2010
$text =~ s/[^c]*//g; // Match anything, except c.

@ ПЛА , Пару комментариев по вашему вопросу:

  1. "//" не является комментарием в Perl. Только "#" есть.
  2. "[^ 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.

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

$text =~ s/ac//g;
2 голосов
/ 21 января 2010

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

$text = "ab ac ad";

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

или

print "ac not found" unless $text =~ /ac/;
1 голос
/ 21 января 2010

Вы можете использовать индекс ()

$text = "ab ac ad";
print "ac not found" if ( index($text,"ac") == -1 );
0 голосов
/ 21 января 2010

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

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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...