如何使用Perl编辑XML文件?

时间:2010-05-25 04:55:47

标签: xml perl

我有一个电影收藏目录,其中包含指向文件夹和文件的本地链接,以便于访问。最近我重组了整个硬盘空间,我需要更新链接,我正在尝试用Perl自动完成。

我可以将数据导出到XML文件中并再次导入。我可以使用File::Find来提取新的文件路径,但我遇到了两个问题。我不知道如何将新文件路径中的$title与XML文件中的相应$title连接起来。我是第一次处理这些文件,我不知道如何继续更换过程。这是我到目前为止所做的事情

use strict; 
use warnings; 
use File::Basename;
use File::Find; 
use File::Spec;
use XML::Simple;
use Data::Dumper;



my $dir_target = 'D:/Movies/';
my %titles_locations = ();

find(\&file_handler, $dir_target);
sub file_handler {
   /\.iso$/ or return;       

   my $fn = $File::Find::name;
   $fn =~ s/\//\\/g;
   $fn =~ /(.*\\)(.*)/;
   my $path = $1;
   my $filename = $2;

   my $title = (File::Spec->splitdir($fn))[2];
   $title =~ s/(.*?)\s\(\d+\)$/$1/;
   $title =~ s/~/:/;
   $title =~ s/`/?/;

   my $link_local = '<link><description>Folder</description><url>'.$path.'</url><urltype>Movie</urltype></link><link><description>'.$filename.'</description><url>'.$fn.'</url><urltype>Movie</urltype></link>' unless $title eq '';

   $titles_locations{$title} = {'filename'=>$filename, 'path'=>$path };
}

   my $xml_in = XMLin('somepath/test.xml', ForceArray => 1, KeepRoot => 1);

   my $title = {'key1' => 'title', 'key2' => 'links'};

   foreach my $link (keys %$title) {
   }

   print Data::Dumper->Dump([$title]);

   my $xml_out = XMLout($xml_in, OutputFile => 'somepath/test_out.xml', KeepRoot=>1);       

这是我需要编辑的数据片段。 如果发现imdb和dvdempire链接 - 请勿触摸。 如果找到本地链接替换,否则插入。 我愿意自己完成代码,但需要一些指示如何继续进行。 感谢。

<title>$title</title>
.......

<links>
<link>
<description>IMDB</description> 
<url>http://www.imdb.com/title/VARIABLE</url> 
<urltype>URL</urltype> 
</link>
<link>
<description>DVD Empire</description> 
<url>http://www.dvdempire.com/VARIABLE</url> 
<urltype>URL</urltype> 
</link>
<link>
<description>Folder</description>
<url>OLD_FOLDERPATH</url>
<urltype>Movie</urltype>
</link>
<link>
<description>OLD_FILENAME</description>
<url>OLD_FILENAMEPATH</url>
<urltype>Movie</urltype>
</link>
</links>

2 个答案:

答案 0 :(得分:3)

摆脱XML::Simple并使用仅为此类任务而制作的XML::Twig。遍历和元素操作内置于Twig中。当Twig完成大部分工作时,要考虑的事情要少得多。

就将旧路径连接到新路径而言,您拥有的数据并不多。如果它们是相同的文件名但位于不同的文件夹中,那么如果它们是唯一的文件名,则可以是匹配新旧路径的方式。除了获取填充%new_paths的所有新路径之外,这里的一切都是:

#!perl

use File::Basename qw(basename);
use XML::Twig;

my %new_paths = (
         # filename => new_path
         ...
         ); 

my $twig = XML::Twig->new(
    twig_handlers => 
      {
      link   => \&rewrite_link,
      },
    pretty_print => 'indented',
    );

$twig->parse( *DATA );
$twig->flush;

sub rewrite_link
    {
    my( $link ) = $_;

    return unless $link->field( 'urltype' ) eq 'Movie';

    # this is from the old file
    my $basename = basename( $link->field( 'url' ) );

    unless( exists $new_paths{ $basename } )
        {
        warn "Didn't find a new location for $basename!\n";
        return;
        }

    $link->first_child( 'url' )->set_text( $new_paths{ $basename } );
    }

__END__
<titles>
<entry>
    <title>$title</title>
    <links>
        <link>
            <description>IMDB</description> 
            <url>http://www.imdb.com/title/VARIABLE</url> 
            <urltype>URL</urltype> 
        </link>
        <link>
            <description>DVD Empire</description> 
            <url>http://www.dvdempire.com/VARIABLE</url> 
            <urltype>URL</urltype> 
        </link>
        <link>
            <description>Folder</description>
            <url>OLD_FOLDERPATH</url>
            <urltype>Movie</urltype>
        </link>
        <link>
            <description>OLD_FILENAME</description>
            <url>OLD_FILENAMEPATH</url>
            <urltype>Movie</urltype>
        </link>
    </links>
</entry>
</titles>

答案 1 :(得分:1)

我会提供一个看似合理的方法 - 如果您希望它更充实,请发表评论。

  1. 在开头声明哈希my %titles_locations = ();

  2. 您应该将您的XML处理移出sub a(请将其称为可读的内容,例如sub file_handler:)

    文件处理程序应该做的是:

    • 像现在一样构建$title$link_local

    • 将它们存储在%titles_locations哈希中,$title为关键,值为包含{'filename'=>$filename, 'path'=>$path }

    • 的hashref
  3. 现在,在您的代码中,在调用find()之后,您将调用XMLin。 $xml_in应该成为一个hashrefs数组(或hashref将你的“root”键映射到一个hashrefs数组。数组中的每个hashref将代表1个标题。

  4. 之后,您将遍历标题的arrayref。

    arrayref的每个元素(称之为$title)都是带有2个密钥{hash} "title""links"的hashref。

    "title"键的值中,找到%titles_locations哈希的新路径和文件名。

    "links"键的值将是hashref的一个hashref映射“链接”。我不打算详细说明这里的数据结构,但通过打印Data::Dumper->Dump([$title]);

    来查看它是微不足道的。

    然后,您将遍历这些链接hashrefs。对于他们每个人(称之为$link

    • 如果$link->{urltype} ne“电影”,请不要管它(next;
    • 如果$link->{description} eq为“文件夹”,请将$link->{url}值替换为您从%titles_locations哈希中找到的新路径。
    • 否则,它是一个文件,将$link->{url}值替换为您从%titles_locations哈希中找到的新文件路径。

    如果$title不在%titles_locations散列中,可能会添加一些错误处理。

  5. 完成所有循环后,只需将$xml_in(现在包含更新的信息)转到XMLout()

  6. DONE