Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Regex 从散列中替换Perl正则表达式_Regex_Perl - Fatal编程技术网

Regex 从散列中替换Perl正则表达式

Regex 从散列中替换Perl正则表达式,regex,perl,Regex,Perl,有没有一种有效的方法可以使用Perl散列中的值来替换一堆字符串 比如说, $regex{foo} = "bar"; $regex{hello} = "world"; $regex{python} = "perl"; open(F, "myfile.txt"); while (<F>) { foreach $key (keys %regex) { s/$key/$regex{$key}/g; } } close(F); my %reg

有没有一种有效的方法可以使用Perl散列中的值来替换一堆字符串

比如说,

$regex{foo} = "bar";
$regex{hello} = "world";
$regex{python} = "perl";

open(F, "myfile.txt");
while (<F>) {
      foreach $key (keys %regex) {
            s/$key/$regex{$key}/g;
      }
}
close(F);
my %regex = (
   foo => 'bar',
   bar => 'foo',
);
$regex{foo}=“bar”;
$regex{hello}=“world”;
$regex{python}=“perl”;
打开(F,“myfile.txt”);
而(){
foreach$键(键%regex){
s/$key/$regex{$key}/g;
}
}
关闭(F);

有没有办法在Perl中完成上述任务?

第一个问题:您确定您所拥有的是低效的吗

其次,最明显的下一步是将所有内容都放在一个正则表达式中:

my $check = join '|', keys %regex;
然后您可以按如下方式进行替换:

s/($check)/$regex{$1}/g;
这仍然可能是“缓慢”的,有足够的键重叠,正则表达式引擎必须不断地重新检查相同的字母。您可以使用类似的方法来消除重叠。但是,优化的成本可能比做任何事情的成本都要高,这取决于更改的数量(散列中的键/值)和修改的行数。过早优化--


当然,请注意,您的示例代码没有对替换后的文本进行任何处理。它不会就地修改文件,因此我假设您单独处理该文件。

定义一个与任何键匹配的regexp

$regex = join("|", map {quotemeta} keys %regex);
$regex
的任何匹配项替换为
$regex{$1}

s/($regex)/$regex{$1}/go;
如果
$regex
在程序执行过程中发生更改,请省略
o
修饰符

请注意,如果存在作为另一个键前缀的键(例如
f
foo
),则在连接的regexp中以先到者为准将被视为匹配(例如
f | foo
匹配
f
foo | f
匹配
foobar
中的
foo
)。如果出现这种情况,您可能需要根据希望赢得的比赛对
键%regex
进行排序。(感谢您指出这一点。)


你可能想考虑的不是一行一行的文件,而是一次处理整个文件,在单行模式的正则表达式上使用<代码>/s>代码>修饰符。

你按原样工作,所以不清楚你的请求是什么。

一个问题是:根据
%regex
和/或
$\uuu
的内容,您发布的代码可能存在双重替换问题。比如说,

$regex{foo} = "bar";
$regex{hello} = "world";
$regex{python} = "perl";

open(F, "myfile.txt");
while (<F>) {
      foreach $key (keys %regex) {
            s/$key/$regex{$key}/g;
      }
}
close(F);
my %regex = (
   foo => 'bar',
   bar => 'foo',
);
可以说,解决方案是将foreach移动到模式中

my $pat =
   join '|',
    map quotemeta,  # Convert text to regex patterns.
     keys %regex;

my $re = qr/$pat/;  # Precompile for efficiency.

my $qfn = 'myfile.txt'
open(my $fh, '<', $qfn) or die "open: $qfn: $!";
while (<$fh>) {
   s/($re)/$regex{$1}/g;
   ... do something with $_ ...
}
我的$pat=
加入“|”,
映射quotemeta,“将文本转换为正则表达式模式。
密钥%regex;
我的$re=qr/$pat/#预编译以提高效率。
my$qfn='myfile.txt'
打开(我的$fh,'开始:
对于大文件,请使用:
对于小文件,请使用:
打开我的$fh,,$ARGV[0]| | die;
打印$fh$t;
收盘价$fh;

为了证明
eval
的观点,也出于好奇,我用OP的代码与
$regex{$1}
方法与
eval
方法进行了一些测试

首先,在
(token | token |…)
匹配表达式中填充每个可能的标记似乎没有什么价值。Perl需要一次检查所有标记——这比简单地一次检查每个标记并用硬编码的值进行替换要有效得多,这是有争议的

其次,执行
$regex{$1}
意味着在每个匹配项上提取hashmap键

无论如何,这里有一些数字(在草莓5.12上运行,4MB文件有100K行):

  • $regex{$1}
    方法需要6秒(使用/go代替/g需要5秒)
  • tie
    方法需要10秒
  • OP方法需要1秒以下的一点(用/go代替/g)
  • eval
    方法所需时间小于1秒(比操作代码快)
  • 这是
    eval
    方法:

    $regex{foo} = "bar";
    $regex{hello} = "world";
    $regex{python} = "perl";
    $regex{bartender} = "barista";
    
    $s = <<HEADER;
    \$start = time;
    open(F, "myfile.txt");
    while (<F>) {
    HEADER
    
    foreach $key (keys %regex) {
       $s .= "s/$key/$regex{$key}\/go;\n"
    }
    
    $s .= <<FOOTER;
    print \$_;
    }
    close(F);
    print STDERR "Elapsed time (eval.pl): " . (time - \$start) . "\r\n";
    FOOTER
    
    eval $s;
    
    $regex{foo}=“bar”;
    $regex{hello}=“world”;
    $regex{python}=“perl”;
    $regex{bartender}=“咖啡师”;
    
    $s=这是一个老问题,所以我很惊讶没有人提出明显的建议:预编译每个regexp(即哈希键)

    如果您知道每个输入行只能有一个可能的匹配,那么在成功匹配后跳过剩余的带有
    last
    的regexp也会有帮助,如果有很多键。例如,在
    for
    循环中:

    s/$key/$regex{$key}/g && last;
    

    如果所有其他方法都失败了,请尝试
    eval
    ()@Nick,就我而言,这是最糟糕的建议。它怎么可能有帮助呢?在perl代码中的任何地方都不可能替换变量--
    eval
    使您能够在字符串中展开变量,然后将该字符串作为perl代码执行,例如eval“s/$key/$regex{$key}/g“@Nick,同样,这会让事情变得更糟。
    s//
    已经插值了。好吧——看看我基于“可怕的”
    eval
    的答案如果你有像abc和abcd这样的键,那么通过减少长度来排序是很重要的:
    map{quotemeta}sort{length($b)length($a)}keys%regex
    @ysth谢谢,我从来没有意识到Perl有一个最左边的匹配策略,而不是最长的匹配!while循环不是解决方案!你在哪里写呢?@cirne100,你可以指定你想对编辑的文本做什么。如果你想在某个地方写,那就去吧。这很有趣,我没有想到
    $regex{$1}
    方法太慢了。使用
    Regexp::Optimizer
    会有什么不同吗?时间是如何根据键的数量而变化的?@Giles,显然是个很好的问题——更不用说平台(windows)perl发行版可能会有所不同。任何关于这种评测的帮助都是非常受欢迎的——从OP那里听到一些东西也会很好——这些方法中哪一种在他/她的环境中最有效。
    $regex{foo} = "bar";
    $regex{hello} = "world";
    $regex{python} = "perl";
    $regex{bartender} = "barista";
    
    $s = <<HEADER;
    \$start = time;
    open(F, "myfile.txt");
    while (<F>) {
    HEADER
    
    foreach $key (keys %regex) {
       $s .= "s/$key/$regex{$key}\/go;\n"
    }
    
    $s .= <<FOOTER;
    print \$_;
    }
    close(F);
    print STDERR "Elapsed time (eval.pl): " . (time - \$start) . "\r\n";
    FOOTER
    
    eval $s;
    
    $regex{qr/foo/} = 'bar';
    $regex{qr/hello/} = 'world';
    $regex{qr/python/} = 'perl';
    
    open(F, "myfile.txt");
    while (<F>) {
          foreach $key (keys %regex) {
                s/$key/$regex{$key}/g;
          }
    }
    close(F);
    
    %regex = (
        qr/foo/    => 'bar',
        qr/hello/  => 'world',
        qr/python/ => 'perl',
    );
    
    s/$key/$regex{$key}/g && last;