在Perl中,你可以为整个匹配字符串使用变量吗?

时间:2017-07-21 06:08:40

标签: perl

我是Perl的新手,虽然不是编程,而是在Learning Perl工作。这本书有练习来匹配小文本文件的连续行。

我想从STDIN提供匹配字符串,然后浏览每个字符串:

while(<STDIN>) {
    chomp;
    $regex = $_;
    seek JUNK, 0, 0;
    while(<JUNK>) {
        chomp();
        if(/$regex/) {
            say;
        }
    }
    say '';
}

这很好用,但我找不到插入整个匹配字符串的方法,例如

/fred/i

进入谓词。我试过了

if($$matcher) # with $matcher = '/fred/'

但Perl抱怨道。

我想这是我的无知,应该欢迎启蒙。

3 个答案:

答案 0 :(得分:3)

语句修饰符,例如/i,是告诉Perl如何执行匹配的代码的一部分,而不是要匹配的模式的一部分。这就是为什么这对你不起作用。

你有三种方法可以解决这个问题(好吧,可能更多,因为这是我们正在谈论的Perl,但我可以直接想到三种方式):

1)使用扩展的正则表达式语法,当您需要不区分大小写的匹配时,请按照问题的评论中的建议输入(?i:fred)

2)使用字符串eval允许使用常规语句修饰符:if (eval "$_ =~ $regex") { say }请注意,此方法还需要您键入周围的斜杠。例如,您必须输入/fred/i;只需输入fred就行不通了。另请注意,在没有首先验证输入的情况下,它是巨大的安全漏洞,因为用户输入的文本是作为Perl代码执行的,就好像它是原计划。 (想象一下,如果用户输入//, system("rm -rf /") - 它会测试空的正则表达式,然后删除计算机上的所有文件。)所以可能不是推荐的方法,除非你真的知道你正在做什么和/或你是唯一一个参加该计划的人。

3)最复杂但也是最正确的解决方案是编写一个解析器来检查用户输入的字符串,以查看是否存在任何特殊标志,然后相应地做出响应。一个非常简单的示例,允许用户附加/i以进行不区分大小写的搜索:

#!/usr/bin/env perl    

use strict;
use warnings;
use 5.010;

while(<STDIN>) {
  chomp;
  my @parts = split '/', $_;

  # If the user input starts with a /, the first part will be empty, so throw
  # it away.
  shift @parts unless $parts[0];

  my $re = shift @parts;
  my %flags;
  for (@parts) {
    for (split '') {
      $flags{i} = 1 if $_ eq 'i';
    }
  }
  my $f = join '', keys %flags;
  say "Matched" if eval qq('foo' =~ /$re/$f);
}

这也使用字符串eval,因此它可能容易受到与#2相同类型的安全问题的影响,但$re不能包含任何/个字符(split '/' }会在第一个$re之前立即结束/,这会阻止代码插入那里,而$f只能包含字母i(或任何其他标记你可能会选择识别你是否扩展了这一点)。所以它应该是安全的。 (但是,如果有人能够展示我错过的漏洞,请在评论中告诉我它!)

答案 1 :(得分:1)

问题

您要做的事情可归纳为:

my $regex = '/fred/i';
my @lines = (
    'A line containing some words and Fred said Hello.',
    'Another line. Here is a regex embedded in the line: /fred/i',
);

for ( @lines ) {
    say if /$regex/;
}

<强>输出

Another line. Here is a regex embedded in the line: /fred/i

我们看到第二行与$regex匹配,而我们希望包含Fred的第一行与字符串fred匹配,并添加(不区分大小写)i标记到正则表达式。问题是/中的字符i$regex被视为字面匹配的字符,即它们不会被解释为围绕正则表达式的特殊字符(作为Perl的一部分)表达)。

注意

字符/是特殊的,作为正则表达式的Perl表达式的一部分,但它不是特殊的里面正则表达式模式。然而,在模式中有一些特殊的字符,即所谓的元字符:

\ | ( ) [ { ^ $ * + ? . 

有关详细信息,请参阅perldoc quotemeta

使用扩展模式的解决方案

只需将第一行更改为:

my $regex = '(?i)fred';  # or alternatively: (?i:fred)

可以使用手册perldoc perlre中描述的“扩展模式”将正则表达式标志添加到正则表达式模式中:

  

扩展模式

     

其中大多数的语法是带有问题的一对括号   标记为括号内的第一个内容。之后的人物   问号表示扩展名。

     

[...]

     

(适用?adlupimnsx-imnsx)
  的(?^ alupimnsx)
  要打开的一个或多个嵌入式模式匹配修饰符(或   如果前面带有“ - ”,则关闭模式的其余部分或   封闭模式组的其余部分(如果有的话)。 这是   特别适用于动态生成的模式,例如那些   从配置文件读入,取自参数,或   在某个表格中指定。

     

[...]

     

这些修饰符将在封闭组的末尾恢复。

或者可以使用非捕获形式:

  

<强>(?:图案)
  的(adluimnsx-imnsx:图案)
  的(^ aluimnsx:图案)
  这是为了聚类,而不是捕获;它将子表达式组合起来   “()”,但不会像“()”那样进行反向引用。

答案 2 :(得分:-1)

以下评论已回答了这个问题:

  

尝试(?i:fred),请参阅Extended patterns   perldoc perlre了解更多信息

- Håkon Hægland 7 hours ago.