我有一个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()
元素?
答案 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_handlers
和purge
。
#!/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放弃到目前为止已经处理过的任何内容(例如任何已经关闭的标签)。