Как я могу избежать метасимволов, когда я интерполирую переменную в операторе соответствия Perl?
Предположим, у меня есть файл, содержащий строки, которые я пытаюсь сопоставить с:
foo
quux
bar
В моем коде у меня есть еще один массив:
foo
baz
quux
Скажем, мы перебираем файл, вызывая каждый элемент $word
и внутренний список, который мы проверяем, @arr
.
if( grep {$_ =~ m/^$word$/i} @arr)
Это работает правильно, но в несколько возможном случае, когда у нас есть тестовый пример fo.
в файле, .
работает как оператор подстановки в регулярном выражении, а fo.
затем соответствует foo
, что неприемлемо.
Это, конечно, потому, что Perl интерполирует переменную в регулярное выражение.
Вопрос:
Как заставить Perl принудительно использовать переменную?
Ответы
Ответ 1
Правильный ответ: не используйте регулярные выражения. Я не говорю, что регулярные выражения плохие, но использование их для (что равно) простой проверки равенства является излишним.
Используйте: grep { lc($_) eq lc($word) } @arr
и будьте счастливы.
Ответ 2
Используйте \Q...\E
для вызова специальных символов непосредственно в строке perl после интерполяции значений переменных:
if( grep {$_ =~ m/^\Q$word\E$/i} @arr)
Ответ 3
От perlfaq6 ответ на Как мне сопоставить регулярное выражение что в переменной?:
Нам не нужны шаблоны жесткого кода в операторе match (или что-либо еще, что работает с регулярными выражениями). Мы можем поместить шаблон в переменную для последующего использования.
Оператор соответствия представляет собой контекст двойной кавычки, поэтому вы можете интерполировать переменную так же, как двойную кавычку. В этом случае вы читаете регулярное выражение как пользовательский ввод и сохраняете его в $regex. Когда у вас есть шаблон в $regex, вы используете эту переменную в операторе соответствия.
chomp( my $regex = <STDIN> );
if( $string =~ m/$regex/ ) { ... }
Любое регулярное выражение специальных символов в $regex по-прежнему является особенным, и шаблон все равно должен быть действительным, или Perl будет жаловаться. Например, в этом шаблоне есть непарная скобка.
my $regex = "Unmatched ( paren";
"Two parens to bind them all" =~ m/$regex/;
Когда Perl компилирует регулярное выражение, он рассматривает скобки как начало совпадения памяти. Когда он не находит закрывающую скобку, он жалуется:
Unmatched ( in regex; marked by <-- HERE in m/Unmatched ( <-- HERE paren/ at script line 3.
Вы можете обойти это несколькими способами в зависимости от нашей ситуации. Во-первых, если вы не хотите, чтобы какой-либо из символов в строке был особым, вы можете избежать их с помощью quotemeta, прежде чем использовать строку.
chomp( my $regex = <STDIN> );
$regex = quotemeta( $regex );
if( $string =~ m/$regex/ ) { ... }
Вы также можете сделать это непосредственно в операторе соответствия, используя последовательности \Q и\E.\Q сообщает Perl, где следует запускать экранирование специальных символов, а \E сообщает, где остановиться (см. Perlop для более подробной информации).
chomp( my $regex = <STDIN> );
if( $string =~ m/\Q$regex\E/ ) { ... }
В качестве альтернативы вы можете использовать qr//, оператор выражения регулярного выражения (подробнее см. perlop). Он цитирует и, возможно, компилирует шаблон, и вы можете применять флаги регулярных выражений к шаблону.
chomp( my $input = <STDIN> );
my $regex = qr/$input/is;
$string =~ m/$regex/ # same as m/$input/is;
Вы также можете уловить любые ошибки, обернув блок eval вокруг всего.
chomp( my $input = <STDIN> );
eval {
if( $string =~ m/\Q$input\E/ ) { ... }
};
warn [email protected] if [email protected];
Или...
my $regex = eval { qr/$input/is };
if( defined $regex ) {
$string =~ m/$regex/;
}
else {
warn [email protected];
}
Ответ 4
QuoteMetaSTRONG >
Возвращает значение EXPR со всеми символами без слова.
http://perldoc.perl.org/functions/quotemeta.html
Ответ 5
Я не думаю, что вам нужно регулярное выражение в этом случае, так как вы не соответствуете шаблону. Вы ищете буквальную последовательность символов, которые вы уже знаете. Создайте хэш со значениями, которые соответствуют и используют для фильтрации @arr
:
open my $fh, '<', $filename or die "...";
my %hash = map { chomp; lc($_), 1 } <$fh>;
foreach my $item ( @arr )
{
next unless exists $hash{ lc($item) };
print "I matched [$item]\n";
}