Regex 从散列中替换Perl正则表达式
有没有一种有效的方法可以使用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
$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;