2010-06-11 12 views
13

मैं रेल ऐप के लिए लॉग व्यूअर पर काम कर रहा हूं और पाया है कि मुझे डिफ़ॉल्ट शीर्ष से नीचे की बजाय नीचे से ऊपर तक लॉग फ़ाइल की लगभग 200 लाइनें पढ़ने की आवश्यकता है।रुबी में नीचे से ऊपर तक फ़ाइल कैसे पढ़ा जाए?

लॉग फाइलें काफी बड़ी हो सकती हैं, इसलिए मैंने पहले ही कोशिश की है और IO.readlines ("log_file.log") [- 200 ..- 1] विधि से इनकार कर दिया है।

क्या रूबी में प्लगइन या मणि की आवश्यकता के बिना फ़ाइल को पीछे पढ़ने के बारे में कोई अन्य तरीका है?

+0

की डुप्लिकेट:: तो मैं Molf कोड बढ़ाया [? रूबी में एक फ़ाइल के अंतिम n लाइनों पढ़ना] (http://stackoverflow.com/questions/754494) – hippietrail

उत्तर

17

यह करने का एकमात्र सही तरीका है कि यह बहुत ही सही तरीका है जो कि n बाइट्स को अंत तक एक समय में पढ़ना है जब तक कि आपके पास इच्छित लाइनों की संख्या न हो। यह अनिवार्य रूप से यूनिक्स tail काम करता है।

IO#tail(n) का एक उदाहरण कार्यान्वयन है, जो एक Array के रूप में पिछले n लाइनों रिटर्न:

class IO 
    TAIL_BUF_LENGTH = 1 << 16 

    def tail(n) 
    return [] if n < 1 

    seek -TAIL_BUF_LENGTH, SEEK_END 

    buf = "" 
    while buf.count("\n") <= n 
     buf = read(TAIL_BUF_LENGTH) + buf 
     seek 2 * -TAIL_BUF_LENGTH, SEEK_CUR 
    end 

    buf.split("\n")[-n..-1] 
    end 
end 

कार्यान्वयन एक छोटे से अनुभवहीन है, लेकिन एक त्वरित बेंचमार्क क्या एक हास्यास्पद अंतर इस सरल कार्यान्वयन पहले से ही कर सकते हैं दिखाता है (

      user  system  total  real 
f.readlines[-200..-1] 7.150000 1.150000 8.300000 ( 8.297671) 
f.tail(200)    0.000000 0.000000 0.000000 ( 0.000367) 

बेंचमार्क कोड:

एक ~ 25 एमबी yes > yes.txt साथ उत्पन्न फ़ाइल) के साथ परीक्षण किया
require "benchmark" 

FILE = "yes.txt" 

Benchmark.bmbm do |b| 
    b.report "f.readlines[-200..-1]" do 
    File.open(FILE) do |f| 
     f.readlines[-200..-1] 
    end 
    end 

    b.report "f.tail(200)" do 
    File.open(FILE) do |f| 
     f.tail(200) 
    end 
    end 
end 

बेशक, other implementations पहले से मौजूद है। मैंने किसी की कोशिश नहीं की है, इसलिए मैं आपको नहीं बता सकता कि कौन सा सर्वोत्तम है।

+0

मुझे लगता है कि तुम्हारा मतलब 'TAIL_BUF_LENGTH = 2 ** 16' या ​​'1 << 16', जिनमें से दोनों का मूल्यांकन' 65536' (64Ki) है। '2^16' बाइनरी अनन्य-या है और '18' का मूल्यांकन करता है। –

+0

महान काम करता है! बेंचमार्क अंतर रीडलाइन की तुलना में पागल है। क्या परिणामी सरणी में प्रत्येक पंक्ति के लिए संबंधित लाइन संख्या को आउटपुट करना संभव है? धन्यवाद! – ericalli

+0

@ दो 2twelve: नहीं, यह नहीं है। इस पूरे अभ्यास का * पूरा उद्देश्य * फ़ाइल को "नीचे से ऊपर तक" पढ़ना है। (आपके शब्द, मेरा नहीं।) आप कैसे जानते होंगे कि कौन सी रेखा (जिसे फ़ाइल के * शीर्ष * से गिना जाता है) आप हैं, यदि आप * नीचे * पर शुरू हुए हैं? या आप नीचे की तरफ से गिनने का मतलब था? उस स्थिति में, यह आसान है: बफर में अनुक्रमणिका 'i' पर रेखा नीचे से 'n-i' वें पंक्ति है। –

3

एक मॉड्यूल Elif उपलब्ध है (पर्ल के File::ReadBackwards का एक बंदरगाह) जो फाइलों के पीछे की ओर से लाइन-लाइन को प्रभावी बनाता है।

0

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

इसलिए छोटे आकार की फाइलों को संभालना मेरे लिए महत्वपूर्ण है (मैं लॉग के दौरान लॉग को पिंग कर सकता हूं)।

class IO 
    def tail(n) 
     return [] if n < 1 
     if File.size(self) < (1 << 16) 
      tail_buf_length = File.size(self) 
      return self.readlines.reverse[0..n-1] 
     else 
      tail_buf_length = 1 << 16 
     end 
     self.seek(-tail_buf_length,IO::SEEK_END) 
     out = "" 
     count = 0 
     while count <= n 
      buf  = self.read(tail_buf_length) 
      count += buf.count("\n") 
      out  += buf 
      # 2 * since the pointer is a the end , of the previous iteration 
      self.seek(2 * -tail_buf_length,IO::SEEK_CUR) 
     end 
     return out.split("\n")[-n..-1] 
    end 
end 
संबंधित मुद्दे