【问题标题】:Perl: How to handle a stream of XML Objects without a root nodePerl:如何处理没有根节点的 XML 对象流
【发布时间】:2014-04-22 23:49:57
【问题描述】:

我需要用 Perl 解析一个巨大的文件。 (所以我将使用流解析器..) 该文件包含多个 XML 文档(对象),但没有根节点。这会导致 XML 解析器在第一个 Object 之后中止,这是应该的。 答案可能是预先/后修复一个假根节点。

<FAKE_ROOT_TAG>Original Stream</FAKE_ROOT_TAG>

由于文件很大(>1GByte),我不想复制/重写它,而是宁愿使用透明(对于 XML 解析器)“合并”或“连接”多个流的类/模块。

stream1 : <FAKE_ROOT_TAG>                 \
stream2 : Original Stream from file        >   merged stream
stream3 : </FAKE_ROOT_TAG>                / 

你能给我指出这个问题的这样一个模块或示例代码吗?

【问题讨论】:

    标签: xml perl merge stream


    【解决方案1】:

    这里有一个简单的例子,说明你可以通过向你的 XML 解析器传递一个虚假的文件句柄来做到这一点。此对象重载 readline 运算符 (&lt;&gt;) 以返回您的假根标记以及文件中的行。

    package FakeFile;
    
    use strict;
    use warnings;
    
    use overload '<>' => \&my_readline;
    
    sub new {
        my $class = shift;
        my $filename  = shift;
    
        open my $fh, '<', $filename or die "open $filename: $!";
    
        return bless { fh => $fh }, $class;
    }
    
    sub my_readline {
        my $self = shift;
        return if $self->{done};
    
        if ( not $self->{started} ) {
            $self->{started} = 1;
            return '<fake_root_tag>';
        }
    
        if ( eof $self->{fh} ) {
            $self->{done} = 1;
            return '</fake_root_tag>';
        }
    
        return readline $self->{fh};
    }
    
    
    1;
    

    如果您的解析器需要一个真正的文件句柄(例如使用 sysread 之类的东西),这将不起作用,但也许您会发现它很有启发性。

    示例用法:

    echo "one
    two
    three" > myfile
    perl -MFakeFile -E 'my $f = FakeFile->new( "myfile" ); print while <$f>' 
    

    【讨论】:

    • 这看起来很有希望!明天我会在办公室看看..我一直在“玩”绑定文件句柄..但还没有得到任何软件。谢谢!
    • 我需要什么 perl 版本才能使用重载 '' \&OverloadingSubroutine 习惯用法? perldoc.perl.org/overload.html 没有说..或者我没看到。
    • @lexu,overload.pm 几乎永远都是核心。
    • 谢谢!我已经接受了 runrig 的回答并更改了问题的标题。他的方法以更少的开销解决了潜在的问题......通过简单地避免它!
    【解决方案2】:

    这是从PerlMonks 中提取的一个技巧:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use XML::Parser;
    use XML::LibXML;
    
    my $doc_file= shift @ARGV;
    
    my $xml=qq{
         <!DOCTYPE doc 
               [<!ENTITY real_doc SYSTEM "$doc_file">]
         >
         <doc>
             &real_doc;
         </doc>
    };
    
    { print "XML::Parser:\n";
      my $t= XML::Parser->new( Style => 'Stream')->parse( $xml);
    }
    
    { print "XML::LibXML:\n";
      my $parser = XML::LibXML->new();
      my $doc = $parser->parse_string($xml);
      print $doc->toString;
    }
    

    【讨论】:

    • 这行得通,谢谢 ..唯一的问题是,XML文件必须在当前工作目录中..所以我不得不将文件名解析为提取路径,在调用 xml 解析器之前执行 chdir(xml_path) 并在解析后返回到我的调用目录。
    • @lexu - 即使文件不在当前目录中,它也应该工作。我在 unix 上测试...也许它不喜欢 Windows 路径?我不知道。如果您在 Windows 上,也许您必须将反斜杠更改为正斜杠或双反斜杠?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多