从Perl中的XML文件中提取一些元素值的最快方法是什么?

时间:2010-03-14 08:44:49

标签: xml perl performance

我有一堆大小约1-2兆字节的XML文件。实际上,不止一堆,有数百万。它们都是格式良好的,甚至许多都是针对它们的模式进行验证的(用libxml2确认)。

所有这些都是由同一个应用程序创建的,因此它们的格式一致(尽管理论上这可能会在未来发生变化)。

我想从Perl脚本中检查每个文件中一个元素的值。速度很重要(我希望每个文件只需不到一秒钟),如上所述,我已经知道文件格式正确。

我非常想要简单地“打开”Perl中的文件并扫描直到我看到我要查找的元素,获取值(靠近文件的开头),然后关闭文件。

另一方面,我可以使用XML解析器(这可能会保护我免受XML格式的未来更改)但我怀疑它会比我想要的慢。

任何人都可以推荐合适的方法和/或解析器吗?

提前致谢。

更新

这是我想要提取的数据的结构/复杂性:

<doc>
  ...
  <someparentnode attrib="notme" attrib2="5">
    <node>Not this one</node>
  </someparentnode>
  <someparentnode attrib="pickme" attrib2="5">
    <node>This is the data I want</node>
  </someparentnode>
  <someparentnode attrib="notme" 
     attrib2="reallyreallylonglineslikethisonearewrapped">
    <node>Not this one either and it may be 
      wrapped too.</node>
  </someparentnode>
  ...    
</doc>

层次结构比这更深层次,但我认为这涵盖了我想要做的各种事情。

3 个答案:

答案 0 :(得分:8)

2个独立的XML感知选项(我写的,所以我可能有偏见; - )是xml_grep(包含在XML::Twig)和xml_grep2(在{{ 3}})。

您可以编写xml_grep -t '*[@attrib="pickme"]' *.xmlxml_grep2 -t '//*[@attrib="pickme"]' *.xml-t选项会将结果显示为文本而不是XML)。 同样在这两种情况下,所有文档都将被解析,但下一版本的xml_grep将添加一个选项来限制每个文件的结果数,并在达到此数字时立即停止解析每个文件。 / p>

否则,如果您需要速度并且需要集成代码,您可以使用XML :: Twig,在您想要的元素上触发处理程序,并在您使用时调用finish_now已经找到它,它将中止解析并继续下一个文件。

XML :: LibXML也是一个选项,虽然你必须完全解析每个文档并使用XPath(容易但可能更慢),使用SAX(可能更快但是代码很痛苦)或使用拉 - 解析器(可能是最好的选择,但我从未使用它)。

更新后

更新:XML :: Twig的代码如下所示:

#!/usr/bin/perl
use strict;
use warnings;

use XML::Twig;

my $twig= XML::Twig->new( twig_handlers => { '*[@attrib="pickme"]' => \&pickme });

foreach my $file (@ARGV)
  { $twig->parsefile( $file); }

sub pickme
  { my( $twig, $node)= @_;
    print $node->text, "\n";
    $twig->finish_now;
  }

答案 1 :(得分:0)

如果您想 fast ,我建议您使用XML :: Bare而不是XML :: Simple或XML :: Twig。

我正在使用它解析几个2-5Mb的XML文件,速度惊人:0.2秒对4分钟,在某些情况下。详情请见http://darkpan.com/files/xml-parsing-perl-gripes.txt

答案 2 :(得分:-2)

awk中

awk 'BEGIN{
 RS="</doc>"
 FS="</someparentnode>"
}

{
  for(i=1;i<=NF;i++){
     if( $i~/pickme/){
        m=split($i,a,"</node>")
        for(o=1;o<=m;o++){
          if(a[o]~/<node>/){
            gsub(/.*<node>/,"",a[o])
            print a[o]
          }
        }
     }
  }
}' file

的Perl

#!/usr/bin/perl
$/ = '</doc>';
$FS = '</someparentnode>';
while (<>) {
    chomp;
    @F = split $FS,;
    for ($i=0;$i<=$#F; $i++) {
        if ($F[$i] =~ /pickme/) {
            $M=(@a=split('</node>', $F[$i]));
            for ($o=0; $o<$M; $o++) {
                if ($a[$o]=~/<node>/) {
                    $a[$o] =~ s/.*<node>//sg;
                    print $a[$o];
                }
            }
        }
    }
}

输出

$ perl script.pl file
This is the data I want

$ ./shell.sh
This is the data I want