2013-05-29 10 views
5

Po napotkaniu plików danych XML zawierających olbrzymie węzły tekstowe, szukałem sposobów na odczytanie i ocenę ich w moich skryptach przetwarzania danych. .Praktyczny sposób odczytywania xml z dużymi węzłami tekstowymi w Perl

plików XML są 3D koordynować pliki do modelowania molekularnego aplikacje mają taką strukturę (przykład):

<?xml version="1.0" encoding="UTF-8"?> 
<hoomd_xml version="1.4"> 
    <configuration> 
     <position> 
     -0.101000 0.011000 -40.000000 
     -0.077000 0.008000 -40.469000 
     -0.008000 0.001000 -40.934000 
     -0.301000 0.033000 -41.157000 
     0.213000 -0.023000 -41.348000 
     ... 
     ... 300,000 to 500,000 lines may follow >> 
     ... 
     -0.140000 0.015000 -42.556000 
     </position> 

     <next_huge_section_of_the_same_pattern> 
     ... 
     ... 
     ... 
     </next_huge_section_of_the_same_pattern> 

    </configuration> 
</hoomd_xml> 

poszczególnych plików XML zawiera kilka węzłów ogromny tekstowych i ma rozmiary między 60 MB i 100 MB w zależności od zawartości.

Próbowałem naiwny approch użyciu XML::Simple pierwszy ale ładowarka zajęłoby wieki wstępnie przeanalizować plik:

... 
my $data = $xml->XMLin('structure_80mb.xml'); 
... 

i powstrzymać się od „Błąd wewnętrzny: wejściowego ogromnego odnośnika”, więc to podejście ISN” t bardzo praktyczne.

Kolejna próba była w użyciu XML::LibXML do czytania - ale tutaj, początkowa ładowarka będzie wyskoczyć natychmiast z komunikatem o błędzie „parser error: xmlSAX2Characters: ogromny węzeł tekst”.

Befor pisanie na ten temat na stackoverflow pisałem aq & d parsera dla siebie i wysłał plik przez niego (po slurping xx MB plik xml do skalara $xml):

... 
# read the <position> data from in-memory xml file 
my @Coord = xml_parser_hack('position', $xml); 
... 

który zwraca dane z każdej linii jako tablicę, kończy w ciągu kilku sekund i wygląda następująco:

sub xml_parser_hack { 
my ($tagname, $xml) = @_; 
return() unless $xml =~ /^</; 

my @Data =(); 
my ($p0, $p1) = (undef,undef); 
$p0 = $+[0] if $xml =~ /^<$tagname[^>]*>[^\r\n]*[r\n]+/msg; # start tag 
$p1 = $-[0] if $xml =~ /^<\/$tagname[^>]*>/msg;    # end tag 
return() unless defined $p0 && defined $p1; 
my @Lines = split /[\r\n]+/, substr $xml, $p0, $p1-$p0; 
for my $line (@Lines) { 
    push @Data, [ split /\s+/, $line ]; 
} 
return @Data; 
} 

działa to dobrze, ale do tej pory nie można uznać za „produkcję gotowy”, oczywiście.

P: Jak odczytałbym plik za pomocą modułu Perla? Który moduł wybiorę?

góry dzięki

RBO


Uzupełnienie: po przeczytaniu komentarza choroba jest, spojrzałem w głąb XML :: libxml. Otwarcie pliku my $reader = XML::LibXML::Reader->new(location =>'structure_80mb.xml'); działa, w przeciwieństwie do tego, co wcześniej sądziłem. Błąd występuje, gdy próbuję uzyskać dostęp do węzła tekstowego poniżej tagu:

... 
while ($reader->read) { 
    # bails out in the loop iteration after accessing the <position> tag, 
    # if the position's text node is accessed 
    # -- xmlSAX2Characters: huge text node --- 
... 
+5

http://search.cpan.org/~mirod/XML-Twig -3.44/Twig.pm - moduł perla do przetwarzania ogromnych dokumentów XML w trybie drzewa. –

+1

Jak otworzyłeś plik z XML :: LibXML? Działa dla mnie dla plików o wielkości 100 MB. – choroba

+0

@choroba - dzięki, sprawdziłem ponownie - i zaktualizowałem temat. –

Odpowiedz

1

Udało mi się zasymulować odpowiedź za pomocą XML :: LibXML. Wypróbuj to i daj mi znać, jeśli to nie zadziała. I stworzył dokument XML z ponad 500k linii w elemencie position, a ja byłem w stanie analizować je i wydrukować zawartość IT:

use strict; 
use warnings; 
use XML::LibXML; 

my $xml = XML::LibXML->load_xml(location => '/perl/test.xml'); 
my $nodes = $xml->findnodes('/hoomd_xml/configuration/position'); 
print $nodes->[0]->textContent . "\n"; 
print scalar(@{$nodes}) . "\n"; 

Używam findnodes użyć wyrażenia XPath wyciągnąć wszystko węzły, które chcę. $nodes to po prostu odwołanie do tablicy, dzięki czemu można go przeglądać w zależności od liczby węzłów, które faktycznie znajdują się w dokumencie.

+0

Dzięki! Ale mój XML :: LibXML 2.0018 Win64 nie może załadować pliku. '$ xml = XML :: LibXML-> load_xml (location => $ fn)' natychmiast kończy się niepowodzeniem w/'błąd parsera: xmlSAX2Characters: ogromny węzeł tekstowy'. Natomiast '$ xml = XML :: LibXML :: Reader-> new (location => $ fn)' ładuje plik, ale nie ma metody: 'Nie można zlokalizować metody obiektu" findnodes "za pośrednictwem pakietu" XML: : LibXML :: Reader "'. –

+0

@rubberboots Czy możesz podać wersję używanego libxml? Możesz go uzyskać, drukując "XML :: LibXML :: LIBXML_DOTTED_VERSION" wewnątrz skryptu Perla. – Joel

+0

Dodałem opcję 'ogromny' zgodnie z propozycją nwellnhof. Teraz twoje 'findnodes' działa idealnie. Dzięki. –

2

Spróbuj XML::LibXML z opcją huge parsera:

my $doc = XML::LibXML->load_xml(
    location => 'structure_80mb.xml', 
    huge  => 1, 
); 

Albo, jeśli chcesz używać XML::LibXML::Reader:

my $reader = XML::LibXML::Reader->new(
    location => 'structure_80mb.xml', 
    huge  => 1, 
); 
+0

To jest to! Dzięki opcji 'huge' w połączeniu z wywołaniem' findnodes' Joela, odczytywanie i przetwarzanie odbywa się w kilka sekund za pośrednictwem LibXML. Dziękuję Ci bardzo! –

Powiązane problemy