Как я могу сопоставить строки, которые не соответствуют определенному шаблону в 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();