在Windows上,使用Strawberry Perl和trick清除目录树中所有文件中xml方括号中的内容



我想清除目录树中XML文件中<loot></loot>元素内部的全部内容。我使用的是64位windows的Strawberry Perl。

例如,这个XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<monster name="Dragon"/>
<health="10000"/>
<immunities>
<immunity fire="1"/>
</immunities>
<loot>
<item id="1"/>
<item id="3"/>
<inside>
<item id="6"/>
</inside>
</item>
</loot>

更改后的文件应为:

<?xml version="1.0" encoding="UTF-8"?>
<monster name="Dragon"/>
<health="10000"/>
<immunities>
<immunity fire="1"/>
</immunities>
<loot>
</loot>

我有这个代码:

#!/usr/bin/perl
use warnings;
use strict;
use File::Find::Rule;
use XML::Twig;
sub delete_loot {
my ( $twig, $loot ) = @_;
foreach my $loot_entry ( $loot -> children ) {
$loot_entry -> delete;
}
$twig -> flush;
}
my $twig = XML::Twig -> new ( pretty_print => 'indented', 
twig_handlers => { 'loot' => &delete_loot } ); 
foreach my $file ( File::Find::Rule  -> file()
-> name ( '*.xml' )
-> in ( 'C:UsersPIODocumentsservmonsters' ) ) {
print "Processing $filen";
$twig -> parsefile_inplace($file); 
}

但它只正确地编辑它遇到的第一个文件,其余文件都是透明的(0 kb透明文件)

XML::Twig-doc说"不太支持多个细枝"。

如果查看细枝对象的状态(例如使用Data::Dumper),您会发现第一次运行和后续运行之间存在很大差异。看起来它认为is已经被完全刷新了(这是真的,因为在第一次运行期间有一个完全的刷新)。它可能没有更多可打印的后续文件,文件最终为空。

在每个循环中重新创建树枝对象对我来说都很有效:

#!/usr/bin/perl
use warnings;
use strict;
use File::Find::Rule;
use XML::Twig;
sub delete_loot {
my ( $twig, $loot ) = @_;
foreach my $loot_entry ( $loot -> children ) {
$loot_entry -> delete;
}
}
foreach my $file ( File::Find::Rule  -> file()
-> name ( '*.xml' )
-> in ( '/home/dabi/tmp' ) ) {
print "Processing $filen";
my $twig = XML::Twig -> new ( pretty_print => 'indented', 
twig_handlers => { loot => &delete_loot, } ); 
$twig -> parsefile($file); 
$twig -> print_to_file($file);
}

此外,我不得不更改XML文件结构以进行处理:

<?xml version="1.0" encoding="UTF-8"?>
<monster name="Dragon">
<health value="10000"/>
<immunities>
<immunity fire="1"/>
</immunities>
<loot>
<item id="1"/>
<item id="3">
<inside>
<item id="6"/>
</inside>
</item>
</loot>
</monster>

注意;将flush更改为print后,问题中的代码对我有效(使用有效的XML)。

但是,我仍然推荐下面的任何一个版本。使用两组有效的XML文件进行了测试。


当首先设置XML::Twig->new(...),然后循环处理文件时,我会得到相同的行为。第一个文件被正确处理,其他文件被完全清空 编辑flushprint替换时,显示的代码实际上可以工作(使用正确的XML文件)。然而,我仍然建议使用下面的任何一个版本,因为XML::Twig不太支持多个文件

原因可能与new类方法有关。然而,我不明白为什么这需要影响对多个文件的处理。回调是在循环之外安装的,但我已经测试过为每个文件重新安装它,它没有帮助。

最后,通过清除状态(由方法new创建),不需要flush-ing,但它在这里显然很痛苦。这不会影响下面的代码,但它仍然被print所取代。

然后做循环中的所有事情。一个简单的版本

use strict;
use warnings;
use File::Find::Rule;
use XML::Twig;
my @files = File::Find::Rule->file->name('*.xml')->in('...');
foreach my $file (@files)
{
print "Processing $filen";
my $t = XML::Twig->new( 
pretty_print => 'indented', 
twig_handlers => { loot => &clear_elt },
);
$t->parsefile_inplace($file)->print;
}
sub clear_elt {
my ($t, $elt) = @_; 
my $elt_name = $elt->name;                # get the name
my $parent = $elt->parent;                # fetch the parent
$elt->delete;                             # remove altogether
$parent->insert_new_elt($elt_name, '');   # add it back empty
}

回调代码被简化了,完全删除元素,然后将其添加回来,为空。请注意,子不需要硬编码的元素名称。因此,这可以按原样用于移除任何元件。

我们可以通过使用另一个类方法nparse来避免在循环中调用new

my $t = XML::Twig->new( pretty_print => 'indented' );
foreach my $file (@files) 
{
print "Processing $filen";
my $tobj = XML::Twig->nparse( 
twig_handlers => { loot => &clear_elt }, 
$file
);
$tobj->parsefile_inplace($file)->print;
}
# the sub clear_elt() same as above

我们必须首先调用new构造函数,即使它没有直接在循环中使用。


请注意,在循环前调用new而不调用twig_handlers,然后在内设置处理程序

$t->setTwigHandlers(loot => sub { ... });

没有帮助。我们仍然只能正确处理第一个文件。

最新更新