2010-09-26 11 views
6

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

मेरा लक्ष्य किसी सीएसवी फ़ाइल के अनुसार पूर्णांक वाले निर्देशिका में सभी फ़ाइलों (वे सीएसवी) में कुछ चीनी अक्षरों को ढूंढना और प्रतिस्थापित करना है। फ़ाइलों को वर्ष-दर-साल अच्छी तरह से क्रमांकित किया जाता है, उदाहरण के लिए 2000-01.csv, और उस निर्देशिका में एकमात्र फाइलें होंगी।

मैं 500 एमबी के प्रत्येक पड़ोस में लगभग 25 फाइलों में लूपिंग करूँगा (और लगभग दस लाख लाइनें)। मैं जिस शब्दकोश का उपयोग कर रहा हूं उसके बारे में 300 तत्व होंगे और मैं यूनिकोड (चीनी वर्ण) को पूर्णांक में बदल दूंगा। मैंने टेस्ट रन के साथ प्रयास किया और, मान लीजिए कि सबकुछ रैखिक रूप से स्केल करता है (?), ऐसा लगता है कि इसे चलाने के लिए लगभग एक सप्ताह लगेंगे।

अग्रिम धन्यवाद। यहाँ मेरी कोड (! हंसी नहीं है) है:

# -*- coding: utf-8 -*- 

import os, codecs 

dir = "C:/Users/Roy/Desktop/test/" 

Dict = {'hello' : 'good', 'world' : 'bad'} 

for dirs, subdirs, files in os.walk(dir): 
    for file in files: 
     inFile = codecs.open(dir + file, "r", "utf-8") 
     inFileStr = inFile.read() 
     inFile.close() 
     inFile = codecs.open(dir + file, "w", "utf-8") 
     for key in Dict: 
      inFileStr = inFileStr.replace(key, Dict[key]) 
     inFile.write(inFileStr) 
     inFile.close() 
+0

यह पाइथन सम्मेलन है जो कम केस अक्षरों के साथ आवृत्ति चर नाम देने के लिए है। भविष्य में भ्रम से बचने के लिए, मैं 'डिक्ट' शब्द को प्रकार से अलग कुछ भी बदल दूंगा। –

+0

क्या आपकी शब्दकोश कुंजी में प्रत्येक 1 चीनी वर्ण शामिल है, या प्रति कुंजी एकाधिक वर्ण हैं? आप चीनी अक्षरों को पूर्णांक के साथ क्यों बदलना चाहते हैं? –

+0

@ जॉन: मेरे पास एक और 35 फाइलें हैं जिनके पास यह जानकारी पहले से ही पूर्णांक के साथ एन्कोड की गई है, और मैं स्टेटटा में अपना विश्लेषण करूँगा, जो यूनिकोड नहीं पढ़ता है। मुझे एक समय में कई वर्ण पढ़ने की जरूरत है, न केवल 1. – rallen

उत्तर

13

अपने वर्तमान कोड में, आप पूरी फ़ाइल को एक बार में स्मृति में पढ़ रहे हैं। चूंकि वे 500 एमबी फाइलें हैं, इसका मतलब है 500 एमबी स्ट्रिंग्स। और फिर आप उनमें से दोहराव प्रतिस्थापन करते हैं, जिसका अर्थ है कि पाइथन को पहले प्रतिस्थापन के साथ एक नई 500 एमबी स्ट्रिंग बनाना है, फिर पहली स्ट्रिंग को नष्ट करना है, फिर दूसरे प्रतिस्थापन के लिए दूसरी 500 एमबी स्ट्रिंग बनाएं, फिर दूसरी स्ट्रिंग, et cetera को नष्ट करें, प्रत्येक प्रतिस्थापन के लिए। यह बहुत सारी यादों का उपयोग करके उल्लेख करने के लिए डेटा की प्रतिलिपि बनाने की काफी प्रतिलिपि बनता है।

यदि आप जानते हैं कि प्रतिस्थापन हमेशा एक पंक्ति में निहित होगा, तो आप फ़ाइल लाइन को इसके द्वारा पुनरावृत्त करके लाइन द्वारा पढ़ सकते हैं। पायथन पढ़ने को बफर करेगा, जिसका मतलब है कि इसे काफी अनुकूलित किया जाएगा। नई फाइल को एक साथ लिखने के लिए, आपको एक नए नाम के तहत एक नई फाइल खोलनी चाहिए। बदले में प्रत्येक पंक्ति पर प्रतिस्थापन करें, और तुरंत इसे लिखें।ऐसा करने से बहुत और स्मृति की मात्रा आगे और पीछे की नकल की इस्तेमाल किया स्मृति की मात्रा कम हो जाएगा के रूप में आप उनकी जगह कार्य करें:

for file in files: 
    fname = os.path.join(dir, file) 
    inFile = codecs.open(fname, "r", "utf-8") 
    outFile = codecs.open(fname + ".new", "w", "utf-8") 
    for line in inFile: 
     newline = do_replacements_on(line) 
     outFile.write(newline) 
    inFile.close() 
    outFile.close() 
    os.rename(fname + ".new", fname) 

आप कुछ करता है, तो वे हमेशा एक लाइन पर हो जाएगा नहीं किया जा सकता है, तो चीजें थोड़ा कठिन हो जाती है; आपको inFile.read(blocksize) का उपयोग करके मैन्युअल रूप से ब्लॉक में पढ़ना होगा, और ब्लॉक के अंत में आंशिक मिलान हो सकता है या नहीं, इस पर सावधानीपूर्वक ट्रैक रखें। ऐसा करना आसान नहीं है, लेकिन आमतौर पर 500 एमबी तारों से बचने के लिए इसके लायक है।

एक और बड़ा सुधार होगा यदि आप प्रतिस्थापन के पूरे समूह की कोशिश करने के बजाए एक बार में प्रतिस्थापन कर सकते हैं। ऐसा करने के कई तरीके हैं, लेकिन जो सबसे अच्छा फिट बैठता है, वह पूरी तरह से निर्भर करता है कि आप क्या बदल रहे हैं और किसके साथ। एकल वर्णों को किसी और चीज में अनुवाद करने के लिए, translate यूनिकोड ऑब्जेक्ट्स की विधि सुविधाजनक हो सकती है। आप इसे यूनिकोड तार करने के लिए (पूर्णांक के रूप में) एक dict मानचित्रण यूनिकोड कोड पॉइंट्स पारित:

>>> u"\xff and \ubd23".translate({0xff: u"255", 0xbd23: u"something else"}) 
u'255 and something else' 

सबस्ट्रिंग है (और न सिर्फ एक अक्षर) की जगह के लिए, आप re मॉड्यूल का उपयोग कर सकते हैं। re.sub समारोह (और संकलित regexps की sub विधि) पहला तर्क है, जो तब प्रत्येक मैच के लिए बुलाया जाएगा के रूप में एक प्रतिदेय (एक समारोह) ले जा सकते हैं:

>>> import re 
>>> d = {u'spam': u'spam, ham, spam and eggs', u'eggs': u'saussages'} 
>>> p = re.compile("|".join(re.escape(k) for k in d)) 
>>> def repl(m): 
...  return d[m.group(0)] 
... 
>>> p.sub(repl, u"spam, vikings, eggs and vikings") 
u'spam, ham, spam and eggs, vikings, saussages and vikings' 
+0

मैं गैर परिवर्तनीय स्ट्रिंग के बारे में भूल गया था।मेरे जवाब से बहुत अच्छा है। – aaronasterling

+2

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

+0

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

0

ओपन फ़ाइलें पढ़ने/लिखने ('आर +') और डबल खोलने/बंद (और संभावना जुड़े बफर फ्लश) से बचें। साथ ही, यदि संभव हो, तो पूरी फ़ाइल को वापस न लिखें, फ़ाइल की सामग्री पर प्रतिस्थापित करने के बाद केवल बदले गए क्षेत्रों को ढूंढें और लिखें। पढ़ें, बदलें, बदले गए क्षेत्रों को लिखें (यदि कोई हो)।

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

+1

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

+0

@ थॉमस: आह, हाँ। हमेशा खुले() झंडे पर पकड़े जाओ। बहुत ज्यादा सी :)। वैसे भी, मेरा सुझाव फ़ाइल को पूरी तरह से पहले पढ़ना था और फिर केवल परिवर्तनों को लिखना था, पढ़ने के दौरान परिवर्तनों को वापस लिखना नहीं। –

+1

जो स्ट्रिंग आप खोलने के लिए पास करते हैं() वास्तव में आप सी में फॉपेन() '(और इस तरह के चूसने वाले अर्थशास्त्र क्यों हैं) को पास करते हैं, इसलिए" बहुत अधिक सी "शायद ही कोई बहाना है :-) –

1

कुछ बातें (अनुकूलन समस्या से संबंधित नहीं):

dir + fileos.path.join(dir, file)

आप infile पुन: उपयोग नहीं करने के लिए चाहते हो सकता है, लेकिन इसके बजाय खुला होना चाहिए (और को लिखने) एक अलग outfile। यह प्रदर्शन में वृद्धि नहीं करेगा, लेकिन अच्छी प्रथा है।

मुझे नहीं पता कि आप I/O बाध्य हैं या cpu बाध्य हैं, लेकिन यदि आपका सीपीयू उपयोग बहुत अधिक है, तो आप थ्रेडिंग का उपयोग करना चाह सकते हैं, प्रत्येक थ्रेड एक अलग फ़ाइल पर चल रहा है (इसलिए एक ट्रैक्टर के साथ कोर प्रोसेसर, आप एक साथ 4 अलग-अलग फाइलें पढ़/लिख रहे होंगे)।

+0

आपके पास थ्रेडिंग सलाह पूरी तरह पीछे की ओर है। पायथन में, धागा आईओ सीमाओं के आसपास पाने के लिए। यह ग्लोबल इंटरप्रेटर लॉक के कारण है। आप CPU/मेमोरी बाध्य अनुप्रयोगों के लिए उपप्रोसेसेस का उपयोग करते हैं जो यह है। (एक सप्ताह में केवल 50 आईओ ऑपरेशंस;) – aaronasterling

+0

अच्छा बिंदु। मैं वैश्विक ताला के बारे में जानता था, लेकिन वास्तव में subprocesses बनाम धागे के बारे में नहीं सोचा था। हर दिन कुछ नया सीखना। – babbitt

+0

@AaronMcSmooth: मैं उम्मीद करता हूं कि यह I/O बाध्य होगा, क्योंकि एक स्ट्रिंग की खोज करना और इसे एक शब्दकोश से बदलना आधुनिक प्रोसेसर के लिए बहुत कम प्रयास है। लेकिन इस मामले में मल्टीथ्रेडिंग तब तक मदद नहीं करेगी जब तक कि कुछ फाइलें अलग-अलग भौतिक डिस्क पर न हों या अनुवादित फ़ाइलों को किसी भिन्न भौतिक डिस्क पर ढूंढना संभव हो। – intuited

2

मुझे लगता है कि आप बहुत स्मृति उपयोग कम कर सकते हैं (और इस प्रकार स्वैप उपयोग को सीमित कर देता है और चीजों को तेज़ी से बना देता है) एक समय में एक पंक्ति पढ़कर और इसे लिखने (रीजएक्स प्रतिस्थापन के बाद पहले ही सुझाया गया) एक अस्थायी फ़ाइल में - फिर मूल को प्रतिस्थापित करने के लिए फ़ाइल को ले जाया जाता है।