में बड़ी टेक्स्ट फ़ाइलों को कुशलता से पार्स करने के लिए कैसे मैं एक आयात स्क्रिप्ट लिख रहा हूं जो ऐसी फाइल को संसाधित करता है जिसमें संभावित रूप से सैकड़ों हजारों लाइनें (लॉग फ़ाइल) हैं। एक बहुत ही सरल दृष्टिकोण (नीचे) का उपयोग करने में पर्याप्त समय और स्मृति मिली जो मुझे लगा कि यह किसी भी समय मेरे एमबीपी को बाहर ले जाएगा, इसलिए मैंने प्रक्रिया को मार दिया।रुबी
#...
File.open(file, 'r') do |f|
f.each_line do |line|
# do stuff here to line
end
end
विशेष रूप से इस फ़ाइल में 642,868 पंक्तियां हैं:
$ wc -l nginx.log /code/src/myimport
642868 ../nginx.log
किसी को भी एक अधिक कुशल (स्मृति/CPU) जिस तरह से इस फ़ाइल में प्रत्येक पंक्ति पर कार्रवाई करने के बारे में पता है?
अद्यतन
ऊपर से f.each_line
के अंदर कोड बस लाइन के खिलाफ एक regex मिलान किया जाता है। यदि मैच विफल रहता है, तो मैं लाइन को @skipped
सरणी में जोड़ता हूं। यदि यह गुजरता है, तो मैं मैचों को हैश में प्रारूपित करता हूं (मैच के "फ़ील्ड्स" द्वारा की गई) और इसे @results
सरणी में जोड़ दें।
# regex built in `def initialize` (not on each line iteration)
@regex = /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) - (.{0})- \[([^\]]+?)\] "(GET|POST|PUT|DELETE) ([^\s]+?) (HTTP\/1\.1)" (\d+) (\d+) "-" "(.*)"/
#... loop lines
match = line.match(@regex)
if match.nil?
@skipped << line
else
@results << convert_to_hash(match)
end
मैं एक अक्षम प्रक्रिया होने के लिए पूरी तरह से खुला हूं। मैं convert_to_hash
के अंदर कोड को हर बार गणना को समझने के बजाय प्रीकंप्यूटेड लैम्ब्डा का उपयोग कर सकता हूं। मुझे लगता है कि मैंने अभी माना है कि यह रेखा ही पुनरावृत्ति थी जो समस्या थी, प्रति पंक्ति कोड नहीं।
पर सबसे अधिक मेमोरी प्रभावी तरीका यह है कि आप इसे 'प्रत्येक_लाइन' के साथ कैसे कर रहे हैं। आप फ़ाइलों को तेज़ी से ब्लॉक में पढ़ सकते हैं, फिर ब्लॉक सीमाओं को पार करने वाली आंशिक रूप से लोड की गई लाइनों को फिर से जोड़ने के साथ-साथ व्यक्तिगत लाइनों को पकड़ने के लिए 'स्ट्रिंग # लाइन'' का उपयोग करें। यह लाइनों को विभाजित करने और टूटे हुए लोगों से जुड़ने के लिए धोने वाला हो जाता है। –