Предположим, что у меня есть файл, содержащий строки, против которых я пытаюсь соответствовать:
foo
quux
bar
В моем коде у меня есть другой массив:
foo
baz
quux
Скажем, мы выполняем итерации через файл, называя каждый элемент $word
, и внутренний список мы проверяем по, @arr
.
if( grep {$_ =~ m/^$word$/i} @arr)
Это работает правильно, но в несколько возможном случае, где у нас есть тестовый сценарий fo.
в файле, .
действует в качестве подстановочного оператора в regex, и fo.
тогда соответствия foo
, который не приемлем.
Это, конечно, потому что Perl интерполирует переменную в regex.
Вопрос:
Как я вынуждаю Perl использовать переменную буквально?
Правильный ответ - не используйте регеxps. Я не говорю, что регэкспы - это плохо, но их использование для (что равно) простой проверки равенства - это перебор.
Use: grep { lc($_) eq lc($word) } @arr
и будь счастлив.
Использовать \Q...\E
для выхода специальных символов непосредственно в perl-строке после интерполяции значений переменных:
if( grep {$_ =~ m/^\Q$word\E$/i} @arr)
Из ответа perlfaq6 в Как сопоставить регулярное выражение, которое находится в переменной?:
Нам не нужно жестко кодировать паттерны в оператор сравнения (или что-либо еще, что работает с регулярными выражениями). Мы можем поместить шаблон в переменную для последующего использования.
Оператор сравнения представляет собой контекст двойной кавычки, так что вы можете интерполировать вашу переменную так же, как и строку с двойными кавычками. В этом случае вы читаете регулярное выражение как пользовательский ввод и сохраняете его в $regex. Как только вы получаете шаблон в $regex, вы используете эту переменную в операторе match.
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 $@ if $@;
Или...
my $regex = eval { qr/$input/is };
if( defined $regex ) {
$string =~ m/$regex/;
}
else {
warn $@;
}
Quotemeta
Возвращает значение EXPR со всеми символами, не содержащими "слова".
Я не думаю, что тебе нужен регекс в этом деле, так как ты не соответствуешь шаблону. Вы ищете буквальную последовательность символов, которую вы уже знаете. Постройте хэш со значениями для соответствия и используйте его для фильтрации @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";
}