Как я могу избежать метасимволов, когда я интерполирую переменную в операторе сопоставления Perl?

Предположим, у меня есть файл, содержащий строки, с которыми я пытаюсь сопоставить:

foo
quux
bar

В моем коде у меня есть другой массив:

foo
baz
quux

Допустим, мы просматриваем файл, вызывая каждый элемент $word, а внутренний список, который мы проверяем, @arr.

if( grep {$_ =~ m/^$word$/i} @arr)

Это работает правильно, но в некотором возможном случае, когда у нас есть тестовый пример fo. в файле, . работает как оператор подстановки в регулярном выражении, а fo. затем соответствует foo, что неприемлемо.

Это, конечно, потому, что Perl интерполирует переменную в регулярное выражение.

Вопрос:

Как заставить Perl использовать переменную буквально?


person Paul Nathan    schedule 04.01.2010    source источник
comment
См. stackoverflow.com/questions/1949731/   -  person Greg Bacon    schedule 04.01.2010
comment
возможный дубликат Как мне обрабатывать специальные символы в Perl регулярное выражение?   -  person daxim    schedule 25.03.2011
comment
Возможный дубликат Как мне обрабатывать специальные символы в регулярном выражении Perl?.   -  person Peter Mortensen    schedule 08.04.2018


Ответы (5)


Правильный ответ - не используйте регулярные выражения. Я не говорю, что регулярные выражения плохи, но использовать их для (что равно) простой проверки на равенство — это излишество.

Используйте: grep { lc($_) eq lc($word) } @arr и будьте счастливы.

person Community    schedule 04.01.2010
comment
Хорошая точка зрения. Решение с регулярным выражением является остатком старого и более сложного кода. - person Paul Nathan; 04.01.2010

Используйте \Q...\E для экранирования специальных символов непосредственно в строке perl после интерполяции значений переменных:

if( grep {$_ =~ m/^\Q$word\E$/i} @arr)
person Ivan Nevostruev    schedule 04.01.2010
comment
Тогда регулярное выражение будет выглядеть примерно так: m/^fo\\E\.$/i. См. \Q описание метасимвола на perldoc.perl.org/perlfaq6.html. - person Ivan Nevostruev; 18.04.2010
comment
В частности, perldoc.perl.org/perlop.html#Quote- and-Quote-like-Operators отмечает, что "abc\Qfoo\tbar$s\Exyz" эквивалентно "abc" . quotemeta("foo\tbar$s") . "xyz". - person Sophie Alpert; 08.03.2014

Из ответа perlfaq6 на Как сопоставить регулярное выражение, которое находится в переменной?:


Нам не нужно жестко кодировать шаблоны в операторе match (или во всем, что работает с регулярными выражениями). Мы можем поместить шаблон в переменную для последующего использования.

Оператор 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.

Вы можете обойти это несколькими способами в зависимости от нашей ситуации. Во-первых, если вы не хотите, чтобы какие-либо символы в строке были специальными, вы можете экранировать их с помощью кавычек перед использованием строки.

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 $@;

Or...

my $regex = eval { qr/$input/is };
if( defined $regex ) {
    $string =~ m/$regex/;
    }
else {
    warn $@;
    }
person brian d foy    schedule 04.01.2010
comment
Является ли eval в предпоследнем примере кода для перехвата ошибок в {...} или может быть что-то не так и в if ( $string =~ m/\Q$input\E/ )? - person sid_com; 05.01.2010
comment
eval поймает все ошибки в своем блоке, но с точки зрения этого вопроса он поймает ошибку в явном коде, который вы видите в операторе match. - person brian d foy; 05.01.2010

Цитата

Возвращает значение EXPR со всеми символами, не являющимися словами, с обратной косой чертой.

http://perldoc.perl.org/functions/quotemeta.html

person Paul Nathan    schedule 04.01.2010

Я не думаю, что в этом случае вам нужно регулярное выражение, поскольку вы не соответствуете шаблону. Вы ищете буквальную последовательность символов, которую вы уже знаете. Создайте хеш со значениями для соответствия и используйте его для фильтрации @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";
      }
person brian d foy    schedule 04.01.2010