如何解决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);
有没有办法在Perl中完成上述任务?
解决方法
第一个问题:您确定自己所拥有的没有效率吗?
第二,最明显的下一步是将所有内容放入单个正则表达式中:
my $check = join \'|\',keys %regex;
然后您可以按以下方式进行替换:
s/($check)/$regex{$1}/g;
这仍然是“慢”的,其中键的足够重叠,而正则表达式引擎必须不断地重新检查相同的字母。您可以使用Regexp :: Optimizer之类的方法来消除重叠。但是优化的成本可能会比仅做所有事情的成本高,这取决于更改的数量(哈希中的键/值)和要修改的行数。过早的优化-!
请注意,当然,您的示例代码对替换后的文本没有任何作用。它不会就地修改文件,因此我假设您要单独处理该文件。
, 为了证明“ 3”的要点以及出于好奇,我用OP的代码与“ 4”方法与“ 3”方法进行了一些测试。
首先,在(token|token|...)
match表达式中塞满每个可能的标记似乎没有什么价值。 Perl需要一次检查所有令牌-与一次简单地检查每个令牌并用硬编码值进行替换相比,这有多有效,这是有争议的。
其次,执行“ 4”表示在每次匹配中都提取哈希映射键。
无论如何,这是一些数字(在草莓5.12上运行,带有4万行10万行的文件):
$regex{$1}
方法需要6秒(用/ go代替/ g需要5秒)
tie
方法需要10秒
OP方法花费不到1秒的时间(使用/ go代替/ g)
“ 3”方法花费不到1秒的时间(比OP代码更快)
这是“ 3”方法:
$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 = join(\"|\",map {quotemeta} keys %regex);
用match4ѭ替换match14ѭ的所有匹配项。
s/($regex)/$regex{$1}/go;
如果在程序执行期间更改$regex
,则省略o
修饰符。
请注意,如果存在以另一个键为前缀的键(例如f
和foo
),则在连接的正则表达式中以先到者为准(例如f|foo
匹配f
但foo|f
匹配foobar
中的ѭ20)。如果可能发生,您可能需要根据要赢的比赛对ѭ26进行排序。 (感谢ysth指出这一点。)
, perl -e \' \\
my %replace = (foo=>bar,hello=>world,python=>perl); \\
my $find = join \"|\",sort keys %replace; \\
my $str = \"foo,hello,python\"; \\
$str =~ s/($find)/$replace{$1}/g; \\
print \"$str\\n\\n\"; \\
\'
您可能要考虑的事情不是逐行处理文件,而是立即处理整个文件,并在正则表达式上使用/s
修饰符进行单行模式。
, 开始:
#!/usr/bin/perl
use strict;
use Tie::File;
my %tr=( \'foo\' => \'bar\',#(...)
);
my $r =join(\"|\",map {quotemeta} keys %tr);
$r=qr|$r|;
大文件使用:
tie my @array,\"Tie::File\",$ARGV[0] || die;
for (@array) {
s/($r)/$tr{$1}/g;
}
untie @array;
与小文件一起使用:
open my $fh,\'<\',$ARGV[0] || die;
local $/ = undef;
my $t=<$fh>;
close $fh;
$t=~s/($r)/$tr{$1}/g;
open $fh,\'>\',$ARGV[0] || die;
print $fh $t;
close $fh;
, 您所拥有的按原样工作,因此不清楚您的要求是什么。
一招:您发布的代码可能会出现双重替换问题,具体取决于%regex
和/或double33 double的内容。例如,
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 $_ ...
}
, 这是一个古老的问题,所以令我惊讶的是,还没有人提出这个显而易见的建议:预编译每个正则表达式(即哈希键)。
$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);
或(IMO)具有更高的可读性:
%regex = (
qr/foo/ => \'bar\',qr/hello/ => \'world\',qr/python/ => \'perl\',);
如果您知道每条输入线只能有一个可能的匹配项,则在有很多键的情况下,成功匹配后用last
跳过其余的正则表达式也将有所帮助。例如在for
循环中:
s/$key/$regex{$key}/g && last;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。