Как я могу избежать метасимволов, когда я интерполирую переменную в операторе соответствия 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];
    }

Ответ 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";
      }