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

时间:2017-01-02 11:55:38

标签: xml perl xml-twig

我希望清除目录树中XML文件中<loot> </loot>个元素内的整个内容。我正在使用Strawberry Perl for Windows 64位。

例如这个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:\Users\PIO\Documents\serv\monsters' ) ) {

    print "Processing $file\n";
    $twig -> parsefile_inplace($file); 
}

但它只能正确编辑它遇到的第一个文件,其余文件保持清晰(0 kb清除文件)

2 个答案:

答案 0 :(得分:3)

XML::Twig doc表示&#34;多枝没有得到很好的支持&#34;。

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

在每个循环中重新创建树枝对象对我有用:

#!/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 $file\n";
    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>

答案 1 :(得分:1)

注意flush更改为print时,问题中的代码适用于我(使用有效的XML)。

但是,我仍然建议使用以下任一版本。测试了两组有效的XML文件。

首先设置XML::Twig->new(...)然后循环并处理文件,我会得到相同的行为。第一个文件处理正确,其他文件完全消隐。 修改 flushprint替换时,显示的代码实际上可以正常工作(使用正确的XML文件)。不过我仍建议使用以下版本,因为XML::Twig只是不支持多个文件。

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

最后,通过清除状态(由方法flush创建),在此明显受伤时不需要new。这不会影响下面的代码,但仍会被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 $file\n";
    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
}

简化了回调代码,完全删除元素然后将其添加回空白。请注意,sub 需要硬编码的元素名称。因此可以使用它来删除任何元素。

我们可以避免使用其他类方法new来调用循环中的nparse

my $t = XML::Twig->new( pretty_print => 'indented' );

foreach my $file (@files) 
{
    print "Processing $file\n";
    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 { ... });

无济于事。我们仍然只能正确处理第一个文件。