【问题标题】:perl: how to parse an xml file sequentiallyperl:如何按顺序解析 xml 文件
【发布时间】:2012-01-27 10:13:46
【问题描述】:

我有一个 XML 文件,它描述了我可以在 UDP 通道上交换的数据结构。 例如: 这是描述我的数据结构的输入 XML 文件。

<ds>
 <uint32 name='a'/>
 <uint32 name='b'/>
 <string name='c'/>
 <int16 name='d'/>
 <uint32 name='e'/>
</ds>

使用 perl 的 XML 解析这个 XML 文件:简单,允许我生成以下哈希

$VAR1 = {
          'uint32' => {
                      'e' => {},
                      'a' => {},
                      'b' => {}
                    },
          'int16' => {
                     'name' => 'd'
                   },
          'string' => {
                      'name' => 'c'
                    }
        };

如您所见,解析后我无法确定字段“e”相对于数据结构开头的相对位置。

我想找出每个元素的偏移量。

我尝试搜索允许我按顺序解析 XML 文件的 perl XML 解析器,类似于“getnexttag()”之类的功能,但找不到。

以编程方式执行此操作的最佳方法是什么?如果不是 perl,那么哪种其他语言最适合做这项工作?

【问题讨论】:

    标签: perl xml-parsing


    【解决方案1】:

    您需要使用带有适当回调的流式解析器,这也将提高解析速度(如果操作正确,还可以减少内存消耗),这是一个很好的/太棒了的东西。

    我推荐您使用XML::SAX,该模块的介绍可在以下链接中找到:

    start_element提供回调,这样你可以一次读取每个元素的值。


    你能给我写一个简单的例子吗?

    是的,我已经有了! ;-)

    下面的sn-p会解析OP提供的数据,打印出每个元素的名字,以及属性key/value。

    这应该很容易理解,但如果您有任何问题,请随时将它们添加为评论,我会更新这篇文章并提供更详细的信息。

    use warnings;
    use strict;
    
    use XML::SAX;
    
    my $parser = XML::SAX::ParserFactory->parser(
      Handler => ExampleHandler->new
    );
    
    $parser->parse_string (<<EOT
    <ds>
      <uint32 name='a'/>
      <uint32 name='b'/>
      <string name='c'/>
      <int16 name='d'/>
      <uint32 name='e'/>
    </ds>
    EOT
    );
    
    # # # # # # # # # # # # # # # # # # # # # # # #
    
    package ExampleHandler;
    
    use base ('XML::SAX::Base');
    
    sub start_element {
      my ($self, $el) = @_;
    
      print "found element: ", $el->{Name}, "\n";
    
      for my $attr (values %{$el->{Attributes}}) {
        print "  '", $attr->{Name}, "' = '", $attr->{Value}, "'\n";
      }
    
      print "\n";
    }
    

    输出

    found element: ds
    
    found element: uint32
      'name' = 'a'
    
    found element: uint32
      'name' = 'b'
    
    found element: string
      'name' = 'c'
    
    found element: int16
      'name' = 'd'
    
    found element: uint32
      'name' = 'e'
    

    我对 XML::SAX 不满意,还有其他可用的模块吗?

    是的,有很多选择。阅读以下列表并选择适合您特定问题的列表:


    不同的解析方式有什么区别?

    我还建议阅读以下有关 XML 解析的常见问题解答。它将提出使用树解析器(例如 XML::Parser::Simple)或流解析器的优缺点:

    【讨论】:

    • 非常感谢您提供如此明确的答案。这很有帮助。
    【解决方案2】:

    用 Perl 肯定是可能的。

    这是XML::LibXML 的示例:

    use strict;
    use warnings;
    use feature 'say';
    use XML::LibXML;
    
    my $xml = XML::LibXML->load_xml( location => 'test.xml' );
    
    my ( $dsNode ) = $xml->findnodes( '/ds' );
    
    my @kids = $dsNode->nonBlankChildNodes;     # The indices of this array will
                                                # give the offset
    
    my $first_kid = shift @kids;                # Pull off the first kid
    say $first_kid->toString;                   # "<uint32 name='a'/>"
    
    my $second = $first_kid->nextNonBlankSibling();     
    my $third  = $second->nextNonBlankSibling();
    
    say $third->toString;                       # "<string name="c"/>"
    

    【讨论】:

      【解决方案3】:

      这里是一个使用XML::Twig的例子

      use XML::Twig;
      
      XML::Twig->new( twig_handlers => { 'ds/*' => \&each_child } )
               ->parse( $your_xml_data );
      
      sub each_child {
          my ($twig, $child) = @_;
          printf "tag %s : name = %s\n", $child->name, $child->{att}->{name};
      }
      

      这个输出:

      tag uint32 : name = a
      tag uint32 : name = b
      tag string : name = c
      tag int16 : name = d
      tag uint32 : name = e
      

      【讨论】:

        猜你喜欢
        • 2018-09-22
        • 1970-01-01
        • 2012-03-27
        • 1970-01-01
        • 2012-06-23
        • 2011-05-06
        • 2017-10-28
        • 2013-07-17
        • 2011-04-13
        相关资源
        最近更新 更多