piątek, 17 lutego 2012

Perl XML:DOM

Plik XML wygląda tak:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE lista.kompozycji SYSTEM "lkompc.dtd" >
<lista.kompozycji>

<kompozycja typ="i.orkiestro">
<tytul>Atlantyda I na orkiestrę symfoniczną</tytul>
<xsklad>4 akordeony w orkiestrze</xsklad>
<autor>
<nazwisko>Augustyn</nazwisko>
<imie>Rafał</imie>
</autor>
<rok>1979</rok>
<sklad>4 acc</sklad>
<wydawca>manus</wydawca>
</kompozycja>

...

<kompozycja typ="solo">
<tytul>Rapsodia</tytul>
<xsklad>akordeon solo</xsklad>
<autor>
<nazwisko>Krzanowski</nazwisko>
<imie>Andrzej</imie>
</autor>
<autor>
<nazwisko>Krzanowska</nazwisko>
<imie>Grażyna</imie>
</autor>
<rok>1983</rok>
<wydawca>PWM</wydawca>
<nagranie>KM</nagranie>
</kompozycja>
...

</lista.kompozycji>

a ma wyglądać tak:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE lista.kompozycji SYSTEM "lkompc.dtd" >
<lista.kompozycji>

<kompozytor id='Augustyn.R'><!-- *** Augustyn:Rafał# -->

<kompozycja typ="i.orkiestro">
<tytul>Atlantyda I na orkiestrę symfoniczną</tytul>
<xsklad>4 akordeony w orkiestrze</xsklad>
<rok>1979</rok>
<sklad>4 acc</sklad>
<wydawca>manus</wydawca>

</kompozycja>
<kompozycja typ="i.orkiestro">
<tytul>Atlantyda II na wielką orkiestrę i chór</tytul>
<xsklad>4 akordeony w orkiestrze</xsklad>
<rok>1983</rok>
<sklad>4 acc</sklad>
<wydawca>manus</wydawca>
<nagranie>LP</nagranie>
</kompozycja>

</kompozytor>

...

<kompozytor id='Krzanowski.A#Krzanowska.G'><!-- *** Krzanowski:Andrzej#Krzanowska:Grażyna# -->
<kompozycja typ="solo">
<tytul>Rapsodia</tytul>
<xsklad>akordeon solo</xsklad>
<rok>1983</rok>
<wydawca>PWM</wydawca>
<nagranie>KM</nagranie>
</kompozycja>

...

</lista.kompozycji>

To znaczy, że z elementu kompozycja mają zniknąć elementy autor. Wszystkie kompozycje tego samego kompozytora mają być elementami-dziećmi elementu kompozytor. Element kompozytor ma identyfikować kompozytora za pomocą atrybutu id, którego wartość jest wyznaczana (w przypadku gdy dzieło jest ma jednego autora) jako:


nazwisko.inicjał

W przypadku gdy kompozycja jest dziełem zbiorowym, identyfikator kompozytora zbiorowego ma mieć postać:


nazwisko.inicjał#nazwisko.inicjał
nazwisko.inicjał#nazwisko.inicjał#nazwisko.inicjał ...

Powyższe realizuje taki oto skrypt:


#!/usr/bin/perl
use XML::DOM;
binmode(STDOUT, ":utf8");

my $file2parse = $ARGV[0];

my $parser = new XML::DOM::Parser;
my $doc = $parser->parsefile ($file2parse);

for my $kompozycja ( $doc->getElementsByTagName ("kompozycja") ) {
my $author_id = '';

## przeglądamy kolejne elementy autor:
for my $autor ($kompozycja->getElementsByTagName("autor")) {

$im = ($autor->getElementsByTagName("imie"))[0]->toString();
$nz = ($autor->getElementsByTagName("nazwisko"))[0]->toString();

$author_id .= "$nz:$im#"; ## autorów może być dużo stąd .= a nie =
$author_id =~ s/<[^<>]+>//g; ## usuń tagi, zostaw sam tekst

##print STDERR "$author_id\n";

## usuń element autor:
$kompozycja->removeChild($autor);
}

## Hash of Arrays, cf http://docstore.mik.ua/orelly/perl2/prog/ch09_02.htm
push @{ $Kompozycje{ $author_id }}, $kompozycja->toString ();

}

### Druk ############################################################

print "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
print "<!DOCTYPE lista.kompozycji SYSTEM \"lkompc.dtd\" >\n";
print "<lista.kompozycji>\n";


for $autor (sort keys %Kompozycje ) {
$autor_i = $autor;
$autor_i =~ s/:([^#:])[^#:]+#/.\1#/g; # tylko inicjały
chop($autor_i);

print "\n\n\n\n<kompozytor id='$autor_i'><!-- *** $autor -->\n\n";
for $kompozycja ( @{ $Kompozycje{ "$autor" }} ) {
print $kompozycja, "\n";
}
print "\n</kompozytor>\n";
}


print "</lista.kompozycji>\n";

## koniec ###

Jeżeli się nie doda binmode, to UTF jest malformed (Ah ten Perl.) Podpowiedź znalazłem tutaj. Nawiasem mówiąc i w innym skrypcie:


s/<imie>([^<>])([^<>]+)<\/imie>/<inicjal>\1<\/inicjal>/gm;

Też zwraca malformed UTF-8 jeżeli np. imieniem jest Łukasz. A jak zaczyna się od A-Z to jest OK.

Brak komentarzy:

Prześlij komentarz