Как мне отредактировать файл XML с помощью Perl? - PullRequest
3 голосов
/ 25 мая 2010

У меня есть каталог коллекции фильмов с локальными ссылками на папки и файлы для быстрого доступа. Недавно я реорганизовал все пространство на жестком диске, и мне нужно обновить ссылки, и я пытаюсь сделать это автоматически с помощью Perl.

Я могу экспортировать данные в файл XML и импортировать их снова. Я могу извлечь новые пути к файлам с помощью File :: Find , но я застрял с двумя проблемами. Я понятия не имею, как связать $title из нового пути к файлу с соответствующим $title из файла XML. Я имею дело с такими файлами в первый раз, и я не знаю, как продолжить процесс замены. Вот что я сделал до сих пор

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 ]

3 голосов
/ 25 мая 2010

Избавьтесь от 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 голос
/ 25 мая 2010

Я предоставлю правдоподобный подход - пожалуйста, прокомментируйте, если вы хотите, чтобы он более подробно раскрылся.

  1. Объявите хеш my %titles_locations = (); в начале.

  2. Вы должны переместить обработку XML из sub a (и, пожалуйста, назовите это как-нибудь читабельное, например sub file_handler:)

    Что должен сделать обработчик файла:

    • Постройте $title и $link_local, как сейчас

    • Сохраните их в хеше %titles_locations, ключом будет $title, а значением - хеш-адрес, содержащий {'filename'=>$filename, 'path'=>$path }

  3. Теперь в своем коде после вызова find() вы будете вызывать XMLin. $xml_in должен стать массивом hashrefs (или hashref, отображающим ваш «корневой» ключ в массив hashrefs. Каждый hashref в массиве будет представлять 1 заголовок.

  4. После этого вы будете перебирать этот массив заголовков.

    Каждый элемент (назовите его $title) arrayref будет хэш-ссылкой с 2 ​​ключами, "title" и "links".

    Из значения ключа "title" найдите новый путь и имя файла из %titles_locations хэша.

    Значение ключа "links" будет хеш-ссылкой, отображающей «ссылку» на массив хеш-ссылок. Я не буду подробно описывать структуру данных здесь, но это тривиально, напечатав Data::Dumper->Dump([$title]);

    Затем вы переберите все хеш-ссылки. Для каждого из них (назовите это $link:

    • Если $link->{urltype} ne "Кино", оставьте его в покое (next;)
    • Если $link->{description} eq «Папка», замените значение $link->{url} новым путем, найденным в хэше %titles_locations.
    • Иначе, это файл, замените значение $link->{url} на новый путь к файлу, который вы нашли из %titles_locations хэша.

    Может быть добавлена ​​обработка ошибок, если $title отсутствует в хэше %titles_locations.

  5. После того, как все циклы завершены, просто возьмите $xml_in (который теперь содержит обновленную информацию) и перейдите к XMLout()

DONE

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...