2012-04-20 15 views
23

मैंने अपनी रूबी (1.9) स्क्रिप्ट में कुछ चरम देरी देखी और कुछ खुदाई के बाद यह नियमित अभिव्यक्ति मिलान के लिए उबला हुआ।नियमित अभिव्यक्ति - रूबी बनाम पर्ल

पर्ल:

$fname = shift(@ARGV); 
open(FILE, "<$fname"); 
while (<FILE>) { 
    if (/(.*?) \|.*?SENDING REQUEST.*?TID=(.*?),/) { 
     print "$1: $2\n"; 
    } 
} 

रूबी: मैं पर्ल में और रूबी में निम्नलिखित परीक्षण स्क्रिप्ट का उपयोग कर रहा

f = File.open(ARGV.shift) 
while (line = f.gets) 
    if /(.*?) \|.*?SENDING REQUEST.*?TID=(.*?),/.match(line) 
     puts "#{$1}: #{$2}" 
    end 
end 

मैं दोनों लिपियों के लिए एक ही इनपुट का उपयोग, एक केवल 442 9 0 लाइनों के साथ फ़ाइल करें। हर एक के लिए समय है:

पर्ल:

[email protected]:~/bin/local/project$ time ./try.pl input >/dev/null 

real 0m0.049s 
user 0m0.040s 
sys  0m0.000s 

रूबी:

[email protected]:~/bin/local/project$ time ./try.rb input >/dev/null 

real 1m5.106s 
user 1m4.910s 
sys  0m0.010s 

मुझे लगता है कि मैं कुछ बहुत बेवकूफ कर रहा हूँ, किसी भी सुझाव?

धन्यवाद

+2

आप की कोशिश की है 'अगर लाइन = ~ /(.*) \ |?।?।?। * अनुरोध भेजा जा रहा * टीआईडी ​​= (*), /'? यह रूबी में भी काम करता है, अगर यह अलग प्रदर्शन विशेषताओं में है तो मैं उत्सुक हूं। –

उत्तर

7
regex = Regexp.new(/(.*?) \|.*?SENDING REQUEST.*?TID=(.*?),/) 

f = File.open(ARGV.shift).each do |line| 
    if regex .match(line) 
     puts "#{$1}: #{$2}" 
    end 
end 

या

regex = Regexp.new(/(.*?) \|.*?SENDING REQUEST.*?TID=(.*?),/) 

f = File.open(ARGV.shift) 
f.each_line do |line| 
    if regex.match(line) 
    puts "#{$1}: #{$2}" 
    end 
+0

+1 मैंने पाया कि पर्ल स्वचालित रूप से ऐसा कर रहा है। – stema

+3

मैंने आपके सुझाव की कोशिश की, लेकिन कोई बदलाव नहीं हुआ, निष्पादन समय अभी भी 1m5.134s – xpapad

+2

कुछ नाइटपिक्स: आपको इसके साथ किए जाने के बाद फ़ाइल डिस्क्रिप्टर को मुक्त करने की आवश्यकता है, या तो 'बंद करें' या 'फ़ाइल का उपयोग करके .open ('filename') {| फ़ाइल | } ', जो सुनिश्चित करता है कि फ़ाइल बंद है। साथ ही, '/#{...}/' 'Regexp' शाब्दिक को दर्शाता है; कॉल 'Regexp.new' अनावश्यक है। –

5
perlretut chapter: Using regular expressions in Perl अनुभाग से

- "खोज और की जगह"

(भले ही नियमित अभिव्यक्ति एक पाश में प्रकट होता है, पर्ल है इसे केवल एक बार संकलित करने के लिए पर्याप्त स्मार्ट।)

मुझे रूबी बहुत अच्छा नहीं पता, लेकिन मुझे संदेह है कि यह प्रत्येक चक्र में रेगेक्स संकलित करता है।
(LaGrandMere के उत्तर से verfiy के लिए कोड का प्रयास करें)।

+0

मुझे शक है कि। इसके लिए एक विशेष वाक्यविन्यास है, इसलिए यह शायद पार्सिंग चरण के दौरान बनाया गया है ... जो लूप से पहले है। – remram

5

एक संभावित अंतर बैकट्रैकिंग की मात्रा है। पर्ल बैकट्रैकिंग करते समय खोज पेड़ को काटने का बेहतर काम कर सकता है (यानी पैटर्न के हिस्से को संभवतः मिलान नहीं किया जा सकता है)। इसका रेगेक्स इंजन अत्यधिक अनुकूलित है।

सबसे पहले, एक अग्रणी « ^ » जोड़ना एक बड़ा अंतर बना सकता है। यदि पैटर्न स्थिति 0 से शुरू नहीं होता है, तो यह प्रारंभिक स्थिति 1 से मेल नहीं खा रहा है! तो स्थिति 1.

ही पंक्तियों के साथ में मैच के लिए, « .*? » सीमित रूप में आप सोच सकते हैं, और एक अधिक सीमित पैटर्न के साथ यह की हर घटना की जगह उलटे पांव लौटने का एक बहुत रोका जा सकता है के रूप में नहीं है कोशिश मत करो ।

तुम क्यों कोशिश मत करो:

/ 
    ^
    (.*?)      [ ]\| 
    (?:(?!SENDING[ ]REQUEST).)* SENDING[ ]REQUEST 
    (?:(?!TID=).)*    TID= 
    ([^,]*)      , 
/x 

(सुनिश्चित नहीं हैं कि अगर यह « [^|] » के साथ पहली « .*? » को बदलने के लिए सुरक्षित था, तो मैं ऐसा नहीं किया।)

(पैटर्न है कि एक एकल स्ट्रिंग से मेल खाने कम से कम, (?:(?!PAT).)PAT के रूप में [^CHAR]CHAR करना है।)

/s का प्रयोग संभवतः चीजों को अगर « . » नई-पंक्तियों से मेल करने की अनुमति दी है गति सकता है, लेकिन मैं लगता है कि यह बहुत मामूली है।

« \space » « [space] » के बजाय का उपयोग करना /x के तहत एक अंतरिक्ष मिलान करने के लिए थोड़ा तेजी से रूबी में हो सकता है। (वे पर्ल के हाल के संस्करणों में समान हैं।) मैंने बाद वाले का उपयोग किया क्योंकि यह कहीं अधिक पठनीय है।

+0

@xpapad, मेरा जवाब tweaked। – ikegami

1

रूबी:

File.open(ARGV.shift).each do |line| 
    if line =~ /(.*?) \|.*?SENDING REQUEST.*?TID=(.*?),/ 
     puts "#{$1}: #{$2}" 
    end 
end 

बदलें =~ ऑपरेटर के लिए match विधि। यह है तेजी से है क्योंकि:

(रूबी बेंचमार्क है मैं अपनी फ़ाइल सामग्री कुछ तो मैं बेतरतीब ढंग से टाइप किया पता नहीं है।)

require 'benchmark' 

def bm(n) 
    Benchmark.bm do |x| 
    x.report{n.times{"asdfajdfaklsdjfklajdklfj".match(/fa/)}} 
    x.report{n.times{"asdfajdfaklsdjfklajdklfj" =~ /fa/}} 
    x.report{n.times{/fa/.match("asdfajdfaklsdjfklajdklfj")}} 
    end 
end 

bm(100000) 

आउटपुट की रिपोर्ट:

 user  system  total  real 
    0.141000 0.000000 0.141000 ( 0.140564) 
    0.047000 0.000000 0.047000 ( 0.046855) 
    0.125000 0.000000 0.125000 ( 0.124945) 

बीच एक है का उपयोग करते हुए =~। इसमें 1/3 से कम लोग लगते हैं। अन्य दो match विधि का उपयोग कर रहे हैं। तो, अपने कोड में =~ का उपयोग करें।

+0

मैंने मैच की बजाय कोशिश की ~ ~ प्रदर्शन में कोई बदलाव नहीं किया। – xpapad

1

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

f = File.open(ARGV.shift) 
my_re = Regexp.new(/(.*?) \|.*?SENDING REQUEST.*?TID=(.*?),/) 
while (line = f.gets) 
    continue if line.index('SENDING REQUEST') == nil 
    if my_re.match(line) 
     puts "#{$1}: #{$2}" 
    end 
end 
f.close() 

मैंने इस विशेष संस्करण को बेंचमार्क नहीं किया है क्योंकि मेरे पास आपका इनपुट डेटा नहीं है। मैंने अतीत में इस तरह की चीजें करने में सफलता हासिल की है, हालांकि, विशेष रूप से लंबी लॉगफाइल के साथ जहां प्री-फ़िल्टरिंग किसी भी नियमित अभिव्यक्ति के बिना इनपुट के विशाल बहुमत को खत्म कर सकती है।

2

(?>re) एक्सटेंशन का उपयोग करने का प्रयास करें। विवरण के लिए Ruby-Documentation, यहाँ एक उद्धरण देखें:

यह निर्माण [..] उलटे पांव लौटने को रोकता है, एक प्रदर्शन को बढ़ाने के हो सकता है। उदाहरण के लिए, पैटर्न /a.*b.*a/ को a युक्त स्ट्रिंग के साथ मिलान करते समय घातीय समय के साथ b एस के साथ मेल खाता है, लेकिन a के पीछे कोई निशान नहीं है। हालांकि, इसे नेस्टेड नियमित अभिव्यक्ति /a(?>.*b).*a/ का उपयोग करके टाला जा सकता है।

File.open(ARGV.shift) do |f| 
    while line = f.gets 
    if /(.*?)(?> \|.*?SENDING REQUEST.*?TID=)(.*?),/.match(line) 
     puts "#{$1}: #{$2}" 
    end 
    end 
end 
संबंधित मुद्दे