2012-12-12 11 views
5

मैं XML::Twig उपयोग कर रहा हूँ एक बहुत बड़ी XML दस्तावेज के माध्यम से पार्स करने के लिए तेजी लाने के कर सकते हैं। मैं <change></change> टैग के आधार पर इसे टुकड़ों में विभाजित करना चाहता हूं।मैं कैसे एक्सएमएल :: टहनी

अभी मेरे पास है:

my $xml = XML::Twig->new(twig_handlers => { 'change' => \&parseChange, }); 
$xml->parsefile($LOGFILE); 

sub parseChange { 

    my ($xml, $change) = @_; 

    my $message = $change->first_child('message'); 
    my @lines = $message->children_text('line'); 

    foreach (@lines) { 
    if ($_ =~ /[^a-zA-Z0-9](?i)bug(?-i)[^a-zA-Z0-9]/) { 
     print outputData "$_\n"; 
    } 
    } 

    outputData->flush(); 
    $change->purge; 
} 

अभी इस parseChange विधि चल रहा है जब यह एक्सएमएल से कि ब्लॉक खींचती है। यह बहुत धीमी गति से चल रहा है। मैंने $/=</change> वाली फ़ाइल से एक्सएमएल पढ़ने और एक्सएमएल टैग की सामग्री को वापस करने के लिए एक फ़ंक्शन लिखने के खिलाफ इसका परीक्षण किया और यह बहुत तेज हो गया।

वहाँ कुछ मैं याद कर रहा हूँ या मैं XML::Twig गलत तरीके से उपयोग कर रहा हूँ है? मैं पर्ल के लिए नया हूँ।

संपादित करें: यहाँ परिवर्तन फ़ाइल से एक उदाहरण परिवर्तन है। फ़ाइल के बाद इन एक सही का एक बहुत होते हैं और उन दोनों के बीच में कुछ भी नहीं होना चाहिए:

<change> 
<project>device_common</project> 
<commit_hash>523e077fb8fe899680c33539155d935e0624e40a</commit_hash> 
<tree_hash>598e7a1bd070f33b1f1f8c926047edde055094cf</tree_hash>  
<parent_hashes>71b1f9be815b72f925e66e866cb7afe9c5cd3239</parent_hashes>  
<author_name>Jean-Baptiste Queru</author_name>  
<author_e-mail>[email protected]</author_e-mail>  
<author_date>Fri Apr 22 08:32:04 2011 -0700</author_date>  
<commiter_name>Jean-Baptiste Queru</commiter_name>  
<commiter_email>[email protected]</commiter_email>  
<committer_date>Fri Apr 22 08:32:04 2011 -0700</committer_date>  
<subject>chmod the output scripts</subject>  
<message>   
    <line>Change-Id: Iae22c67066ba4160071aa2b30a5a1052b00a9d7f</line>  
</message>  
<target>   
    <line>generate-blob-scripts.sh</line>  
</target> 
</change> 
+0

मुझे नहीं लगता कि एक्सएमएल :: ट्विग' पर जाने से पहले एक्सएमएल को रेगेक्स के साथ प्री-प्रोसेस करना अच्छा विचार है। यह आपके कोड को बहुत कम मजबूत बनाता है। क्या होगा यदि एक टिप्पणी के भीतर ' 'है, उदाहरण के लिए? साथ ही, यह असंभव है कि एक्सएमएल पार्सिंग आपकी स्क्रिप्ट को धीमा करने वाली चीज है। क्या आप अधिक जानकारी दे सकते हैं: फ़ाइल का आकार और आप किस प्रकार की प्रसंस्करण कर रहे हैं? – dan1111

+0

मैं फिलहाल कहीं भी रेगेक्स का उपयोग नहीं कर रहा हूं। एक विधि टहनियों का उपयोग कर रही थी और दूसरा इसे पढ़ रहा था और इसे स्वयं पार्स कर रहा था। मैंने इस टुकड़े को समग्र लिपि से निकाला है, इसलिए यह एकमात्र चीज है जो चल रही है। इसके अलावा, फ़ाइल का आकार 2.3 जीबी है। मैं एक्सएमएल से डेटा निकाल रहा हूं और इसमें से कुछ को हैश में जोड़ रहा हूं। – user1897691

+0

क्षमा करें, यह "regex" कहने की गलती थी। मेरा मतलब था कि यदि आप इसे पार्स करने से पहले कुछ नियम (जैसे लाइन विभाजक) का उपयोग कर फ़ाइल तोड़ते हैं, तो आप एक्सएमएल की अखंडता को तोड़ सकते हैं। आपकी एक्सएमएल फाइल कितनी बड़ी है? – dan1111

उत्तर

1

XML::Twig एक व्यवस्था है जिसके द्वारा आप टैग संभाल कर सकते हैं के रूप में वे दिखाई देते हैं, तो त्यागने तुम अब क्या शामिल है स्मृति मुक्त करने की जरूरत है।

यहाँ एक उदाहरण the documentation से लिया (जो भी एक बहुत अधिक उपयोगी जानकारी है) है: क्योंकि (फिर

my $t= XML::Twig->new(twig_handlers => 
          { section => \&section, 
          para => sub { $_->set_tag('p'); } 
          }, 
         ); 
    $t->parsefile('doc.xml'); 

    # the handler is called once a section is completely parsed, ie when 
    # the end tag for section is found, it receives the twig itself and 
    # the element (including all its sub-elements) as arguments 
    sub section 
    { my($t, $section)= @_;  # arguments for all twig_handlers 
     $section->set_tag('div'); # change the tag name.4, my favourite method... 
     # let's use the attribute nb as a prefix to the title 
     my $title= $section->first_child('title'); # find the title 
     my $nb= $title->att('nb'); # get the attribute 
     $title->prefix("$nb - "); # easy isn't it? 
     $section->flush;   # outputs the section and frees memory 
    } 

यह शायद आवश्यक है जब एक मल्टी-गीगाबाइट फ़ाइल के साथ काम हो जाएगा, के अनुसार, प्रलेखन) पूरी चीज को स्मृति में संग्रहीत करने से फ़ाइल के आकार के 10 गुणा अधिक हो सकते हैं।

संपादित करें: आपके संपादित प्रश्न के आधार पर कुछ टिप्पणियां।

  • उत्पादन filehandle निस्तब्धता आप धीमी हो जाएगी अगर आप लाइनों की एक बहुत कुछ लिख रहे हैं: यह वास्तव में क्या आपके फ़ाइल संरचना के बारे में अधिक जानने के बिना आप धीमी हो रही है नीचे, लेकिन यहाँ कुछ चीजें आजमाने जा रहे हैं स्पष्ट नहीं है। विशेष रूप से प्रदर्शन कारणों से पर्ल कैश फ़ाइल लेखन, और आप इसे छोड़ रहे हैं।
  • इसके बजाय (?i) तंत्र का उपयोग करने का
  • , एक नहीं बल्कि उन्नत सुविधा शायद निष्पादन दंड है, क्यों नहीं पूरे मैच केस संवेदी है? /[^a-z0-9]bug[^a-z0-9]/i बराबर है। तुम भी /\bbug\b/i साथ यह आसान बनाने के लिए है, जो लगभग बराबर है सक्षम हो सकता है, फर्क सिर्फ इतना है जा रहा है कि अंडरस्कोर मेल नहीं खाने वाले वर्ग में शामिल हैं।
  • अन्य सरलीकरण की एक जोड़ी है कि रूप में अच्छी तरह मध्यवर्ती कदम उठाए दूर करने के लिए किया जा सकता है कर रहे हैं।

यह हैंडलर कोड आपके गति से तुलना कैसे करता है?

sub parseChange 
{ 
    my ($xml, $change) = @_; 

    foreach(grep /[^a-z0-9]bug[^a-z0-9]/i, $change->first_child_text('message')) 
    { 
     print outputData "$_\n"; 
    } 

    $change->purge; 
} 
+0

मैंने इसे थोड़ा सा देखा, हालांकि मुझे यह स्वीकार करना होगा कि मैं 'पैरा' लाइन के बारे में उलझन में हूं। मुझे लगता है कि मैं यही कर रहा हूं। आप मेरे नमूना कोड में देख सकते हैं कि मैंने एक हैंडलर परिभाषित किया था। – user1897691

+0

@ user1897691, क्या आपने अपने हैंडलर में मुफ्त मेमोरी के लिए 'फ्लश' या 'शुद्ध' किया था? मैं 'एक्सएमएल :: ट्विग' पर एक विशेषज्ञ नहीं हूं, लेकिन यदि आप अपने हैंडलर का कोड पोस्ट करते हैं तो कोई भी आपकी मदद करने में सक्षम हो सकता है। – dan1111

+0

ठीक है मैंने इसे अपने मूल प्रश्न में जोड़ा। मुझे यकीन है कि कोई व्यक्ति इस बारे में टिप्पणी करेगा कि फाइलियो महंगा कैसे है लेकिन यह कोड के दोनों संस्करणों में किया जा रहा है और मुझे अलग-अलग समय मिल रहे हैं। फाइलियो एक कारण नहीं है कि कोई दूसरे की तुलना में इतना तेज क्यों चल रहा है। – user1897691

3

यह खड़ा के रूप में, अपने कार्यक्रम XML दस्तावेज़ के सभी संसाधित कर रहा है। change तत्वों है कि आप में कोई दिलचस्पी नहीं कर रहे हैं बाहर डेटा समेत

आप में twig_handlers पैरामीटर बदलते हैं तो आपके twig_roots के लिए कन्स्ट्रक्टर, फिर पेड़ संरचना केवल ब्याज के तत्वों के लिए बनाई जाएगी और शेष को अनदेखा कर दिया जाएगा।

my $xml = XML::Twig->new(twig_roots => { change => \&parseChange }); 
+0

मैं इसे आज़माउंगा लेकिन दस्तावेज़ को एक-दूसरे के ठीक बाद बदलावों का एक गुच्छा होना चाहिए। मैंने इसे चलाने शुरू कर दिया है और यह पहले की तरह ही गति के बारे में दिखता है। – user1897691

+0

फिर आपको अपने एक्सएमएल को ['SQLite'] (https://metacpan.org/module/DBD::SQLite) में आयात करना चाहिए, वहां से उस पर काम करें और बाद में इसे निर्यात करें। एक्सएमएल एक यादृच्छिक-पहुंच डेटाबेस प्रारूप नहीं है। – Borodin

0

यदि आपका एक्सएमएल वास्तव में बड़ा है, तो XML::SAX का उपयोग करें। इसे पूरे डेटा सेट को स्मृति में लोड करने की आवश्यकता नहीं है; इसके बजाए, यह अनुक्रमिक रूप से फ़ाइल लोड करता है और प्रत्येक टैग के लिए कॉलबैक ईवेंट उत्पन्न करता है। मैंने 1 जीबी से अधिक आकार के साथ XML को पार्स करने के लिए XML :: SAX का सफलतापूर्वक उपयोग किया। यहाँ एक एक्सएमएल का एक उदाहरण :: अपने डेटा के लिए SAX हैंडलर है:

#!/usr/bin/env perl 
package Change::Extractor; 
use 5.010; 
use strict; 
use warnings qw(all); 

use base qw(XML::SAX::Base); 

sub new { 
    bless { data => '', path => [] }, shift; 
} 

sub start_element { 
    my ($self, $el) = @_; 
    $self->{data} = ''; 
    push @{$self->{path}} => $el->{Name}; 
} 

sub end_element { 
    my ($self, $el) = @_; 
    if ($self->{path} ~~ [qw[change message line]]) { 
     say $self->{data}; 
    } 
    pop @{$self->{path}}; 
} 

sub characters { 
    my ($self, $data) = @_; 
    $self->{data} .= $data->{Data}; 
} 

1; 

package main; 
use strict; 
use warnings qw(all); 

use XML::SAX::PurePerl; 

my $handler = Change::Extractor->new; 
my $parser = XML::SAX::PurePerl->new(Handler => $handler); 

$parser->parse_file(\*DATA); 

__DATA__ 
<?xml version="1.0"?> 
<change> 
    <project>device_common</project> 
    <commit_hash>523e077fb8fe899680c33539155d935e0624e40a</commit_hash> 
    <tree_hash>598e7a1bd070f33b1f1f8c926047edde055094cf</tree_hash> 
    <parent_hashes>71b1f9be815b72f925e66e866cb7afe9c5cd3239</parent_hashes> 
    <author_name>Jean-Baptiste Queru</author_name> 
    <author_e-mail>[email protected]</author_e-mail> 
    <author_date>Fri Apr 22 08:32:04 2011 -0700</author_date> 
    <commiter_name>Jean-Baptiste Queru</commiter_name> 
    <commiter_email>[email protected]</commiter_email> 
    <committer_date>Fri Apr 22 08:32:04 2011 -0700</committer_date> 
    <subject>chmod the output scripts</subject> 
    <message> 
    <line>Change-Id: Iae22c67066ba4160071aa2b30a5a1052b00a9d7f</line> 
    </message> 
    <target> 
    <line>generate-blob-scripts.sh</line> 
    </target> 
</change> 

आउटपुट

Change-Id: Iae22c67066ba4160071aa2b30a5a1052b00a9d7f 
+0

यदि यह तेज़ है, तो यह चाल करेगा। हालांकि मैं आपके उदाहरण में खींची गई रेखा के अलावा एक्सएमएल से अन्य जानकारी भी ढूंढ रहा हूं। मैं अपने विनिर्देश के एक निश्चित टैग में डेटा कैसे खींच सकता हूं? – user1897691

+0

प्रदान किया गया उदाहरण 'if ($ self -> {path} ~~ [qw [बदलें संदेश पंक्ति]] के माध्यम से टैग का पता लगाता है) {...} 'शर्त। तो, 'author_name' लेने के लिए, एक शर्त' $ self -> {path} ~~ [qw [author_name] बदलें] 'जोड़ें। – creaktive

0

नहीं एक XML :: टहनी जवाब है, लेकिन ...

यदि आप एक्सएमएल फाइलों से सामान निकालने जा रहे हैं, तो आप एक्सएसएलटी पर विचार करना चाहेंगे। Xsltproc और निम्न XSL स्टाइलशीट का उपयोग करके, मुझे लगभग एक मिनट में <change> के 1 जीबी से बग-युक्त परिवर्तन लाइनें मिलीं। बहुत सारे सुधार संभव हैं, मुझे यकीन है।

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" > 

    <xsl:output method="text"/> 
    <xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'" /> 
    <xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" /> 

    <xsl:template match="/"> 
    <xsl:apply-templates select="changes/change/message/line"/> 
    </xsl:template> 

    <xsl:template match="line"> 
    <xsl:variable name="lower" select="translate(.,$uppercase,$lowercase)" /> 
    <xsl:if test="contains($lower,'bug')"> 
     <xsl:value-of select="."/> 
     <xsl:text> 
</xsl:text> 
    </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

अपने XML प्रोसेसिंग

  1. निकालने सादा पाठ
  2. लड़ाई पाठ चपटा के रूप में किया जा सकता है, तो
  3. लाभ

तो XSLT पहले के लिए उपकरण हो सकता है उस प्रक्रिया में कदम।

संबंधित मुद्दे