拆分和处理XML文件

时间:2015-11-08 07:55:11

标签: xml perl

我有一个original.xml文件结构如下:

<root>
    <parent attr1="val1" attr2="val2" ... >
        <child key1="val3" key2="val4" ... />
        <child key1="val5" key2="val6" ... />
        ...
    </parent>
    ...
    <parent attr1="val7" attr2="val8" ... />
    ...
</root>

对于此文件的每个parent节点,我需要创建一个新文件,根据attr1属性的值命名并插入此数据:

<newroot>
    <newparent attr1="val1" attr2="val2" ... />
</newroot>

为此,我正在使用这个perl单行命令:

perl -p -i -e 'open(F, ">", "new/".($1).".xml") if /<parent attr1="(.*)" attr2="(.*)" ... /; print {F} "<newroot><newparent attr1=\"".($1)."\" attr2=\"".($2)."\" /></newroot>";' "original.xml"

对于没有parent ren的所有原始child,此功能足够好。但是对于parent ren child的每一个parent(我说的是我的例子中的第一个parent),它会将输出乘以<newroot> <newparent attr1="val1" attr2="val2" ... /> </newroot> <newroot> <newparent attr1="" attr2="" ... /> </newroot> <newroot> <newparent attr1="" attr2="" ... /> </newroot> ... <newroot> <newparent attr1="" attr2="" ... /> </newroot> 所拥有的行数,就像这样:

newroot

我不太明白为什么会这样。如何使perl命令只输出一个带有所需数据的def ask(what,why): root=Tk() root.title(why) label=Label(root,text=what) label.pack() entry=Entry(root) entry.pack() button=Button(root,text='OK',command=root.destroy) button.pack() root.mainloop() return entry.get() 元素?

2 个答案:

答案 0 :(得分:1)

我建议使用XSLT来解决这个问题,例如LibXSLT支持exsl:document(参见http://exslt.org/exsl/elements/document/index.html),这样你就可以编写一个XSLT样式表了

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exsl="http://exslt.org/common"
  extension-element-prefixes="exsl"
  exclude-result-prefixes="exsl"
  version="1.0">

<xsl:template match="/">
  <xsl:apply-templates select="root/parent[@attr1]" mode="new"/>
</xsl:template>

<xsl:template match="parent" mode="new">
  <xsl:message>Writing file <xsl:value-of select="@attr1"/></xsl:message>
  <exsl:document href="{@attr1}.xml" method="xml" indent="yes">
    <newroot>
      <xsl:apply-templates select="."/>
    </newroot>
  </exsl:document>
</xsl:template>

<xsl:template match="parent">
  <newparent>
    <xsl:copy-of select="@*"/>
  </newparent>
</xsl:template>

</xsl:stylesheet>

拆分XML输入文档,为每个root/parent元素创建一个带有attr1属性的结果文件,在名为newroot的文件中创建新根,转换{{1元素到parent并复制其属性。

您可以使用

在Perl中运行样式表
newparent

正如您在评论中指出的那样,您遇到了内存和/或性能问题,这里有一种替代方法,它使用 use XML::LibXSLT; use XML::LibXML; my $xslt = XML::LibXSLT->new(); my $source = XML::LibXML->load_xml(location => 'original.xml'); my $style_doc = XML::LibXML->load_xml(location => 'sheet1.xsl'); my $stylesheet = $xslt->parse_stylesheet($style_doc); my $results = $stylesheet->transform($source); print $stylesheet->output_as_bytes($results); 作为转发读取拉解析器,并且不会将完整的XML加载到内存树结构:

LibXML::Reader

我希望它应该避免记忆问题。

答案 1 :(得分:1)

不要使用正则表达式。使用解析器。我非常喜欢XML::Twig。 (XML::LibXML也很不错。)

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig->new->parsefile('your_source.xml');

foreach my $parent_elt ( $twig->findnodes('//parent') ) {
    #remove children if that's what you want?
    $_->delete for $parent_elt->children();
    my $newname = $parent_elt->att('attr1');
    print "Opening:", $newname, "\n";
    my $new_doc = XML::Twig->new->set_root( XML::Twig::Elt->new('newroot') );
    $parent_elt->cut;
    $parent_elt->paste( $new_doc->root );

    $new_doc -> set_pretty_print ('indented_a'); 
    open( my $output, '>', "$newname.xml" ) or die $!;
    print {$output} $new_doc->sprint;
    close($output);
}

根据您的样本数据,这大概就是您想要的。

好的,所以你没有提到的东西:

  

马丁非常感谢。这非常有效。但有一件事我忘了提及。我的original.xml有超过一百万个父标签。

非常重要 - XML是一个标记匹配过程,这意味着它无法确定它是否已完成,并且XML在到达结束之前一直有效。这通常意味着解析整个文档以验证标记是否匹配。

XML的一个缺点是它的内存占用量通常是文件大小的10倍。

但是,XML::Twig还有另一个有用的功能 - twig_handlerspurge

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

sub extract_parent_elt {
    my ( $twig, $parent_elt ) = @_;

    #remove children if that's what you want?
    $_->delete for $parent_elt->children();

    #pick out the attr for our file name
    my $newname = $parent_elt->att('attr1');
    print "Opening:", $newname, ".xml\n";

    #create a new document - insert a 'newroot' as it's root element. 
    my $new_doc = XML::Twig->new->set_root( XML::Twig::Elt->new('newroot') );
    #cut and paste this element into the new doc. 
    $parent_elt->cut;
    $parent_elt->paste( $new_doc->root );
    #note - because we're purging and not saving the 'old' doc, a
    #cut doesn't modify the original. 

    #open output:
    open( my $output, '>', "$newname.xml" ) or die $!;
    #note - pretty print has some limitations. 
    #specifically - there are some XML things that it breaks. 
    #your code doesn't _appear_ to have these. 
    $new_doc -> set_pretty_print('indented_a'); 
    print {$output} $new_doc->sprint;
    close($output);

    #discard everything so far.
    $twig->purge;
}

my $twig = XML::Twig->new( 
                twig_handlers => { 'parent' => \&extract_parent_elt } );

$twig -> parsefile('original.xml');

处理程序触发找到匹配的“关闭”状态。元素,并获得大量的XML。 purge告诉twig放弃到目前为止已经处理过的任何内容(例如任何已经关闭的标签)。