2013-05-29 8 views
5

Dopo aver incontrato file di dati xml contenenti enormi nodi di testo, ho cercato alcuni modi per leggerli e valutarli nei miei dati script di elaborazione.Modo pratico di leggere xml con enormi nodi di testo in Perl

I file XML sono file di coordinare 3D per la modellazione molecolare applicazioni un hanno questa struttura (esempio):

<?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> 

Ogni file XML contiene diversi nodi enorme di testo e ha dimensioni comprese tra 60 MB e 100 MB a seconda i contenuti.

Ho provato l'approch Naïve utilizzando XML::Simple primo momento, ma il caricatore avrebbe preso per sempre per analizzare inizialmente il file:

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

e fermare con "errore interno: enorme di ricerca di ingresso", quindi questo approccio isn' molto praticabile

Il tentativo successivo è stato quello di utilizzare XML::LibXML per la lettura - ma qui, il caricatore iniziale sarebbe salvare immediatamente con il messaggio di errore "parser di errore: xmlSAX2Characters: enorme nodo di testo".

Befor scrivere su questo argomento su StackOverflow, ho scritto aq & d parser per me e inviato il file attraverso di esso (dopo slurping il file XML MB xx nella scalare $xml):

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

che restituisce i dati di ogni riga come un array, completa in pochi secondi e si presenta in questo modo:

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; 
} 

questo funziona bene finora, ma non può considerarsi 'pronti per la produzione', naturalmente.

D: Come posso leggere il file utilizzando un modulo Perl? Quale modulo dovrei scegliere?

Grazie in anticipo

rbo


Addendum: dopo aver letto il commento di choroba, ho guardato più in profondità XML :: LibXML. L'apertura del file my $reader = XML::LibXML::Reader->new(location =>'structure_80mb.xml'); funziona, contrariamente a quanto pensavo prima. L'errore si verifica se provo ad accedere al nodo di testo sotto il tag:

... 
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 - modulo perl per l'elaborazione di enormi documenti XML in modalità albero. –

+1

Come hai aperto il file con XML :: LibXML? Funziona per me per file di 100 MB. – choroba

+0

@choroba - grazie, ho controllato di nuovo - e ho aggiornato l'argomento. –

risposta

1

sono stato in grado di simulare una risposta utilizzando XML :: LibXML. Prova questo e fammi sapere se non funziona. Ho creato un documento XML con più di 500k righe nel elemento position, e sono stato in grado di analizzare e stampare il contenuto di esso:

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"; 

che sto usando findnodes per usare un'espressione XPath per tirare fuori tutto i nodi che voglio $nodes è solo un ref dell'array, quindi puoi passarlo in loop a seconda di quanti nodi hai effettivamente nel tuo documento.

+0

Grazie! Ma il mio XML :: LibXML 2.0018 Win64 non può caricare il file. '$ xml = XML :: LibXML-> load_xml (location => $ fn)' fallisce immediatamente w/'error parser: xmlSAX2Characters: huge text node'. Mentre '$ xml = XML :: LibXML :: Reader-> new (location => $ fn)' carica il file, ma non ha il metodo: 'Impossibile trovare il metodo dell'oggetto" findnodes "tramite il pacchetto" XML: : LibXML :: Reader "'. –

+0

@rubberboots Puoi fornire quale versione di libxml stai usando? Puoi ottenerlo stampando 'XML :: LibXML :: LIBXML_DOTTED_VERSION' all'interno del tuo script Perl. – Joel

+0

Ho aggiunto l'opzione 'enorme' come proposto da nwellnhof. Ora i tuoi 'findnodes' funzionano perfettamente. Grazie. –

2

Prova XML::LibXML con l'opzione huge parser:

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

Oppure, se si desidera utilizzare XML::LibXML::Reader:

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

Questo è tutto! Con l'opzione 'huge', combinata con la chiamata' findnodes' di Joel, la lettura e l'elaborazione avvengono in pochi secondi tramite LibXML. Grazie mille! –