Perl 替换 - 查找 table 问题
Perl substitution - lookup table issue
我有一个大约 35MB 的 KML 文件,其中所有地标都命名为 "kml1234" 等。我想用 "Area 9987" 之类的可读字符串替换名称,并且我有查找 table。我在这里 () 找到了一段 perl,它适用于大多数地标。但是,我发现它在特定情况下失败了。这是代码。
$repl{kml1} = "Area A";
$repl{kml12} = "Area B";
$repl{kml123} = "Area C";
$repl{kml69} = "Area D";
$repl{kml4458} = "Area E";
$s = <<HEADER;
$start = time;
open(F, "input.txt");
open(OUTPUT, ">output.txt");
while (<F>) {
HEADER
foreach $key (keys %repl) {
$s .= "s/$key/$repl{$key}\/;\n"
}
$s .= <<FOOTER;
print $_;
}
close(F);
close(OUTPUT);
print "Elapsed time (eval.pl): " . (time - $start) . "\r\n";
FOOTER
eval $s;
我已经使用测试字符串(放入 input.txt)单独测试了它:
<Placemark id="kml123">
预期的结果是:
<Placemark id="Area C">
但是,如果我使用相同的输入 (kml123) 再次 运行 脚本,我会得到以下 3 个结果中的任何一个:
<Placemark id="Area A23">
<Placemark id="Area B3">
<Placemark id="Area C">
似乎这种替代有时会以某种方式 t运行cating $key 到 kml1 或 kml12?我注意到我从来没有得到预期的 "Area D" 或 "Area E",我怀疑这是因为它们与 kml123 不够相似,只是前 3 个。有什么线索吗?
本程序的核心是使用Perl的正则表达式。问题是 Perl kml1
作为搜索字符串(s///
表达式的第一部分)匹配 kml123
。如果您将 search/replace 更改为以下内容:
s/"$key">$/"$repl{$key}"/;
它将起作用,因为它只有在完全匹配的模式用引号引起来时才会匹配。
有点想,但阅读this page on regular expressions will make you a master at them. Playing games like regex golf也会帮助你提高技能,更有效地解决这个问题。
@ahjohnston25 的回答中已经提到了主要问题,但是你接管了带有评估和模糊内容的丑陋代码,所以我让它变得更简单和更清晰:
#!/usr/bin/perl
use strict; use warnings; use autodie;
my %repl = (
"kml1" => "Area A",
"kml12" => "Area B",
"kml123" => "Area C",
"kml69" => "Area D",
"kml4458" => "Area E",
);
open( my $F, '<', "input.txt" );
open( my $OUTPUT, '>', "output.txt" );
while ( <$F> ) {
foreach my $key ( sort keys %repl ) {
s/\b$key\b/$repl{$key}/g;
}
print $OUTPUT $_;
}
close( $F );
close( $OUTPUT );
我希望在这种形式下更容易理解正在发生的事情。
我有一个大约 35MB 的 KML 文件,其中所有地标都命名为 "kml1234" 等。我想用 "Area 9987" 之类的可读字符串替换名称,并且我有查找 table。我在这里 () 找到了一段 perl,它适用于大多数地标。但是,我发现它在特定情况下失败了。这是代码。
$repl{kml1} = "Area A";
$repl{kml12} = "Area B";
$repl{kml123} = "Area C";
$repl{kml69} = "Area D";
$repl{kml4458} = "Area E";
$s = <<HEADER;
$start = time;
open(F, "input.txt");
open(OUTPUT, ">output.txt");
while (<F>) {
HEADER
foreach $key (keys %repl) {
$s .= "s/$key/$repl{$key}\/;\n"
}
$s .= <<FOOTER;
print $_;
}
close(F);
close(OUTPUT);
print "Elapsed time (eval.pl): " . (time - $start) . "\r\n";
FOOTER
eval $s;
我已经使用测试字符串(放入 input.txt)单独测试了它:
<Placemark id="kml123">
预期的结果是:
<Placemark id="Area C">
但是,如果我使用相同的输入 (kml123) 再次 运行 脚本,我会得到以下 3 个结果中的任何一个:
<Placemark id="Area A23">
<Placemark id="Area B3">
<Placemark id="Area C">
似乎这种替代有时会以某种方式 t运行cating $key 到 kml1 或 kml12?我注意到我从来没有得到预期的 "Area D" 或 "Area E",我怀疑这是因为它们与 kml123 不够相似,只是前 3 个。有什么线索吗?
本程序的核心是使用Perl的正则表达式。问题是 Perl kml1
作为搜索字符串(s///
表达式的第一部分)匹配 kml123
。如果您将 search/replace 更改为以下内容:
s/"$key">$/"$repl{$key}"/;
它将起作用,因为它只有在完全匹配的模式用引号引起来时才会匹配。
有点想,但阅读this page on regular expressions will make you a master at them. Playing games like regex golf也会帮助你提高技能,更有效地解决这个问题。
@ahjohnston25 的回答中已经提到了主要问题,但是你接管了带有评估和模糊内容的丑陋代码,所以我让它变得更简单和更清晰:
#!/usr/bin/perl
use strict; use warnings; use autodie;
my %repl = (
"kml1" => "Area A",
"kml12" => "Area B",
"kml123" => "Area C",
"kml69" => "Area D",
"kml4458" => "Area E",
);
open( my $F, '<', "input.txt" );
open( my $OUTPUT, '>', "output.txt" );
while ( <$F> ) {
foreach my $key ( sort keys %repl ) {
s/\b$key\b/$repl{$key}/g;
}
print $OUTPUT $_;
}
close( $F );
close( $OUTPUT );
我希望在这种形式下更容易理解正在发生的事情。