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

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

foo
quux
bar

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

foo
baz
quux

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

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

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

Это, конечно, потому что Perl интерполирует переменную в regex.

Вопрос:

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

12
задан brian d foy 4 January 2010 в 18:28
поделиться

5 ответов

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

Use: grep { lc($_) eq lc($word) } @arr и будь счастлив.

12
ответ дан 2 December 2019 в 02:53
поделиться

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

if( grep {$_ =~ m/^\Q$word\E$/i} @arr)
33
ответ дан 2 December 2019 в 02:53
поделиться

Из ответа 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 $@;
    }
17
ответ дан 2 December 2019 в 02:53
поделиться

Quotemeta

Возвращает значение EXPR со всеми символами, не содержащими "слова".

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

5
ответ дан 2 December 2019 в 02:53
поделиться

Я не думаю, что тебе нужен регекс в этом деле, так как ты не соответствуешь шаблону. Вы ищете буквальную последовательность символов, которую вы уже знаете. Постройте хэш со значениями для соответствия и используйте его для фильтрации @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";
      }
2
ответ дан 2 December 2019 в 02:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: