我有近500个xhtml文件,我想在所有文件中找到重复的ID。目标是获取一个文件读取id =" xxx"其余文件中不应出现相同的ID。如果通过错误消息找到,则chapter1 id出现在其他一些章节文件中。
我试过这个结果也来了,但需要运行程序将近15分钟。 我想要有效的编码请帮助。
foreach my $xhtml(@xhtml_files){
my $htmlcnt = _open_file("$dirname\\$xhtml");
my @Duplicate_xhtml_files = ();
#-------------The external (ID) matched with (filename)-------------------
@Duplicate_xhtml_files = _get_file_list($ARGV[0],1,0,'\.xhtml$',$xhtml);
my @array_ids = $htmlcnt =~ m{( id="[^>"]+")}isg;
my $array_joinids = "##".join("##",@array_ids)."##";
foreach my $file(@Duplicate_xhtml_files){
my $duplicate_htmlcnt = _open_file("$dirname\\$file");
while($duplicate_htmlcnt =~ m{( id="[^>"]+")}isg){
my $pre = $`; my $check_id = $1;
if($array_joinids =~ m{\#\#$check_id\#\#}is){
($ln, $cl) = LineCol($pre);
$Error .="\n\t[$ln:$cl]\: Error:[MV-1024]\: $file => The external ($check_id) matched with ($xhtml).\n";
}
}
}
}
提前感谢。
答案 0 :(得分:2)
在循环外做尽可能多的工作 。
循环中的循环(隐藏循环中)。这可能很慢。
如果有500 @xhtml_files
,那么假设每个文件中的@Duplicate_xhtml_files
和100 id
中有100个。我们不要忘记搜索$array_joinids
,这是ID列表的隐藏循环!假设$array_joinids
中有100个ID。
foreach my $xhtml (@xhtml_files) {
...500 times...
foreach my $file(@Duplicate_xhtml_files) {
...50,000 times...
while($duplicate_htmlcnt =~ m{( id="[^>"]+")}isg){
...5,000,000 times...
# This is really looping over all the ids, so
# you're looking at IDs 500,000,000 times.
if($array_joinids =~ m{\#\#$check_id\#\#}is){
}
}
}
}
这只是猜测,但你明白了:在内循环中工作会大大增加成本。 尽可能高地做你想做的一切。
例如,如果您需要在列表中查找特定元素,则表示您需要遍历整个列表。循环中有太多循环。而且你是通过将列表转换为##
分隔的字符串(循环)然后使用正则表达式(循环)反复搜索该字符串而以一种非常模糊的方式进行的。
此...
my @array_ids = $htmlcnt =~ m{( id="[^>"]+")}isg;
my $array_joinids = "##".join("##",@array_ids)."##";
while($duplicate_htmlcnt =~ m{ id="([^>"]+)"}isg) {
my $check_id = $1;
if($array_joinids =~ m{\#\#$check_id\#\#}is) {
...
}
}
这样做得好得多......
my @ids = $htmlcnt =~ m{( id="[^>"]+")}isg;
while($duplicate_htmlcnt =~ m{ id="([^>"]+)"}isg) {
my $check_id = $1;
if( grep { $_ eq $check_id } @ids ) {
...
}
}
但是仍然在你的关键内循环中循环遍历所有ID(grep
)。你可以使用List::Util::first让它更快一点,所以它会停在比赛上,但它只是重新安排泰坦尼克号的椅子。真正的表现胜利是摆脱最内层的循环。
相反,请使用哈希。然后,您不必遍历关键最内层循环中的所有ID,而是可以执行快速散列查找。无论有多少元素,散列查找都是相同的速度。
# Also only store the ID, not all the HTML around it.
my %ids = map { $_ => 1 } = $htmlcnt =~ m{ id="([^>"]+)"}isg;
while($duplicate_htmlcnt =~ m{ id="([^>"]+)"}isg) {
my $check_id = $1;
if( $ids{$check_id} ) {
...it's a duplicate!...
}
}
另一个明显的目标是消除所有内部内部循环。扫描所有文件一次,存储所有ID,然后使用%all_ids
检查重复项。这样可以避免多次解析相同的XHTML文件。
# This will hold what IDs are in what files.
my %all_ids;
# Record which IDs are in which files.
for my $xhtml (@all_xhtml_files) {
...
while( $htmlcnt =~ m{ id="([^>"]+)"}isg ) {
$all_ids{$1}{$xhtml} = 1;
}
}
# Now go through the list of IDs and look for ones that are in
# more than one file.
for my $id (keys %all_ids) {
my $in_files = $all_ids{$id};
if( @$in_files > 1 ) {
print "Duplicate ID $id seen in @$in_files";
}
}
您必须修改它以获取重复检测的详细信息,但您明白了。
顺便说一句,除非您在修复此问题时使用Perl 5.20,否则请勿使用$`
。它可以严重减慢所有正则表达式。有关详细信息和替代方案,请参阅perlvar。
答案 1 :(得分:0)
使用或不使用perl有几种方法可以做到这一点。 如果你想使用perl,你有几个选择:
`
use File::Slurp;
my %id_values;
foreach my $file (@xhtml_files @Duplicate_xhtml_files) {
my @lines = read_file("$file");
my $line_index = 1;
foreach my $line (@lines) {
my @array_ids = $line =~ m{( id="[^>"]+")}isg;
my $id = '##'.join('##',@array_ids).'##';
push @{$id_values{$id}}, { $file => $line_index };
$line_index++;
}
}
foreach my $key (keys(%id_values)) {
if (@{$id_values} > 1) {
foreach my $dup (@{$id_values}) {
print $key . ':: ' . $file . ' - ' . $line;
}
}
}
`
这未经过测试,但它应该为您提供一般性的想法。
不使用perl,您可以使用bash来确定值,然后grep它们。类似的东西:
cat *.xhtml |cut -d'=' -f2 |sort uniq -c |sort >> duplicate_ids.txt
(“cut”命令是近似的,因为你没有提供一个例子)
答案 2 :(得分:0)
好的,我建议不要做你正在做的事情,答案是使用解析器'。
问题在于,如果没有更好的样本输入,我无法给出更明确的答案,但它应该是这样的:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my %seen;
sub seen_id {
my ( $twig, $tag ) = @_;
my $id = $tag->att('id');
$tag -> print;
return unless $id;
print "Duplicate spotted $id", if $seen{$id}++;
}
my $twig = XML::Twig -> new ( twig_handlers => { 'tag[@id]' => \&seen_id } );
foreach my $xhtml ( glob "*.xhtml" ) {
$twig -> parsefile ( $xhtml );
}
print join ( "\n", sort keys %seen );
根据我xhtml
文件的大小,我可能会执行清除/丢弃/提前救助等操作。 (磁盘IO通常是限制因素)。
或者可能改为:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my %seen;
foreach my $xhtml ( glob "*.xhtml" ) {
my $twig = XML::Twig -> new();
$twig -> parsefile ( $xhtml );
#get the first element (of any) with an 'id' attribute)
my $id = $twig -> get_xpath('//*[@id]',0) -> att('id');
print "$xhtml is a dupe\n" if $seen{$id}++;
}
print join ( "\n", sort keys %seen );