मैं पायथन में बड़ी पाठ फ़ाइलों को कैसे पढ़ सकता हूं, लाइन से लाइन, बिना मेमोरी में लोड किए?


239

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

नीचे दिया गया कोड इस मामले के लिए कैसे काम करेगा? क्या xreadlinesस्मृति में एक-एक करके ही पढ़ा जा रहा है? क्या जनरेटर अभिव्यक्ति की आवश्यकता है?

f = (line for line in open("log.txt").xreadlines())  # how much is loaded in memory?

f.next()  

इसके अलावा, मैं लिनक्स tailकमांड की तरह इसे रिवर्स ऑर्डर में पढ़ने के लिए क्या कर सकता हूं ?

मुझे मिला:

http://code.google.com/p/pytailer/

तथा

" अजगर का सिर, पूंछ और पीछे की ओर एक पाठ फ़ाइल की पंक्तियों द्वारा पढ़ा गया "

दोनों ने बहुत अच्छा काम किया!


और मैं इसे पूंछ से पढ़ने के लिए क्या कर सकता हूं? लाइन द्वारा लाइन, अंतिम लाइन में शुरू।
ब्रूनो रोचा - रोचक्रूनो

यह एक अलग प्रश्न होना चाहिए
cmcginty

1
डुप्लिकेट stackoverflow.com/questions/5896079/…
cmcginty

जवाबों:


310

मैंने यह जवाब इसलिए दिया क्योंकि रसीद के होते हुए, स्पष्ट रूप से फाइल को बंद नहीं करता है

with open("log.txt") as infile:
    for line in infile:
        do_something_with(line)

30
सवाल अभी भी है, "इनफाइल में लाइन के लिए" मेरी 5GB लाइनों को मेमोरी में लोड करेगा? और, मैं पूंछ से कैसे पढ़ सकता हूं?
ब्रूनो रोचा - रोचक्रूनो

66
@rochacbruno, यह केवल एक समय में एक पंक्ति पढ़ता है। जब अगली पंक्ति पढ़ी जाती है, तो पिछले एक कूड़े को इकट्ठा किया जाएगा जब तक कि आपने इसे कहीं और नहीं
भेजा

1
@rochacbruno, रिवर्स ऑर्डर में लाइनों को पढ़ना दुर्भाग्य से कुशलता से करना आसान नहीं है। आम तौर पर आप समझदार आकार के विखंडू में फ़ाइल के अंत से पढ़ना चाहते हैं (किलोबाइट्स मेगाबाइट्स कहते हैं) और नईलाइन वर्णों पर विभाजित करें (या जो भी लाइन आपके
चार्ट

4
धन्यवाद! मुझे टेल सॉल्यूशन stackoverflow.com/questions/5896079/…
ब्रूनो रोचा - rochacbruno

1
@bawejakunal, क्या आपका मतलब है कि यदि एक बार में एक लाइन मेमोरी में लोड होने के लिए बहुत लंबी है? यह एक पाठ फ़ाइल के लिए असामान्य है । forलूप का उपयोग करने के बजाय, जो लाइनों के आधार पर पुनरावृत्त करता है, आप chunk = infile.read(chunksize)उनकी सामग्री की परवाह किए बिना सीमित आकार के विखंडू को पढ़ने के लिए उपयोग कर सकते हैं । आप खुद को newlines के लिए विखंडू के अंदर खोजना होगा।
जॉन ला रोय

60

आपको केवल फ़ाइल ऑब्जेक्ट का उपयोग इट्रेटर के रूप में करने की आवश्यकता है।

for line in open("log.txt"):
    do_something_with(line)

हाल के पायथन संस्करणों में संदर्भ प्रबंधक का उपयोग करना बेहतर है।

with open("log.txt") as fileobject:
    for line in fileobject:
        do_something_with(line)

यह स्वचालित रूप से फ़ाइल को भी बंद कर देगा।


2
यह पूरी फ़ाइल को मेमोरी में लोड नहीं कर रहा है?
ब्रूनो रोचा - रोचक्रूनो

17

एक पुराना स्कूल दृष्टिकोण:

fh = open(file_name, 'rt')
line = fh.readline()
while line:
    # do stuff with line
    line = fh.readline()
fh.close()

2
नाबालिग टिप्पणी: अपवाद सुरक्षा के लिए यह "खुला (फ़ाइल नाम, 'आर टी') एफ एच के रूप में के साथ:" आपके मामले में बयान 'के साथ' उपयोग की सिफारिश की है,
prokher

16
@prokher: हाँ, लेकिन मैंने इसे "पुराना स्कूल" कहा है।
PTBNL

15

आप इसके बजाय एक पुनरावृत्ति का उपयोग करने से बेहतर हैं। प्रासंगिक: http://docs.python.org/library/fileinput.html

डॉक्स से:

import fileinput
for line in fileinput.input("filename"):
    process(line)

यह एक ही बार में पूरी फ़ाइल को मेमोरी में कॉपी करने से बचाएगा।


यद्यपि डॉक्स स्निपेट को "विशिष्ट उपयोग" के रूप में दिखाते हैं, इसका उपयोग करते हुए लूप खत्म होने पर close()लौटे FileInputवर्ग ऑब्जेक्ट की विधि को कॉल नहीं करता है - इसलिए मैं इसे इस तरह से उपयोग करने से बचूंगा। पायथन 3.2 में उन्होंने अंत fileinputमें संदर्भ प्रबंधक प्रोटोकॉल के साथ संगत किया है जो इस मुद्दे को संबोधित करता है (लेकिन कोड अभी भी नहीं दिखाया जाएगा जिस तरह से दिखाया गया है)।
मार्टीन्यू

7

यदि आप फ़ाइल में नई सीमाएँ नहीं रखते हैं तो आप यहाँ क्या करेंगे:

with open('large_text.txt') as f:
  while True:
    c = f.read(1024)
    if not c:
      break
    print(c)

जबकि मुझे यह तरीका पसंद है, आप अपने टेक्स्ट में लाइन के टूटने का जोखिम उठाते हैं। मैंने इसे व्यक्तिगत रूप से देखा, जिसका अर्थ है कि यदि आप फ़ाइल में sstring की खोज कर रहे हैं जैसे कि मैं था, तो मुझे कुछ याद होगा क्योंकि लाइन वे पर विखंडू में टूट गई थी। क्या इस से निकाल पाने के लिए कोई तरीका है? रीडलाइन का उपयोग करना अच्छी तरह से काम नहीं करता था क्योंकि मुझे
edo101

6

कृपया इसे आज़माएँ:

with open('filename','r',buffering=100000) as f:
    for line in f:
        print line

कृपया समझाएँ?
निखिल वीजे

3
पायथन के आधिकारिक डॉकमुनेट्स से: लिंक वैकल्पिक बफ़रिंग तर्क फ़ाइल के वांछित बफर आकार को निर्दिष्ट करता है: 0 का अर्थ है असंबद्ध, 1 का अर्थ है लाइन बफ़र्ड, किसी भी अन्य सकारात्मक मान का अर्थ है उस आकार (बाइट्स में) के बफर का उपयोग करना। एक नकारात्मक बफ़रिंग का अर्थ है सिस्टम डिफॉल्ट का उपयोग करना, जो आमतौर पर छोटे उपकरणों के लिए लाइन बफ़र और अन्य फ़ाइलों के लिए पूरी तरह से बफ़र किया जाता है। यदि छोड़ दिया जाता है, तो सिस्टम डिफॉल्ट का उपयोग किया जाता है
ज्योति दास

मेरे दिन को बचाया, मेरे मामले में, दो फ़ाइल हैंडलर (एक पढ़ा, अन्य लिख) के साथ ~ 4gb फाइलें अजगर लटका हुआ था और अब यह ठीक है! धन्यवाद।
Xelt

@ ज्योतिदास इस पद्धति को पसंद करते हुए, आप अपने पाठ में पंक्ति के टूटने का जोखिम उठाते हैं। मैंने इसे व्यक्तिगत रूप से देखा, जिसका अर्थ है कि यदि आप फ़ाइल में sstring की खोज कर रहे हैं जैसे कि मैं था, तो मुझे कुछ याद होगा क्योंकि लाइन वे पर विखंडू में टूट गई थी। क्या इस से निकाल पाने के लिए कोई तरीका है? रीडलाइन का उपयोग करना अच्छी तरह से काम नहीं कर रहा था क्योंकि मुझे
मिसकाउंट

3

मुझे विश्वास नहीं हो रहा था कि यह जितना आसान हो सकता है @ जॉन-ला-रोय के उत्तर से लगता है। इसलिए, मैंने cpलाइन रीडिंग और राइटिंग द्वारा लाइन का उपयोग करके कमांड को फिर से बनाया । यह बहुत जल्दी है।

#!/usr/bin/env python3.6

import sys

with open(sys.argv[2], 'w') as outfile:
    with open(sys.argv[1]) as infile:
        for line in infile:
            outfile.write(line)

नोट: क्योंकि अजगर के readlineलाइन एंडिंग का मानकीकरण होता है, यह डॉक्स लाइन एंडिंग के साथ दस्तावेजों \r\nको यूनिक्स लाइन एंडिंग में बदलने का साइड इफेक्ट है \n। इस विषय को खोजने के लिए मेरा पूरा कारण यह था कि मुझे एक लॉग फ़ाइल को बदलने की आवश्यकता थी जो लाइन एंडिंग का एक जंबल प्राप्त करता है (क्योंकि डेवलपर ने नेत्रहीन रूप से विभिन्न .NET लाइब्रेरीज़ का उपयोग किया है)। मुझे यह जानकर धक्का लगा कि मेरी प्रारंभिक गति परीक्षण के बाद, मुझे वापस जाने और rstripलाइनों की आवश्यकता नहीं थी । यह पहले से ही सही था!
ब्रूनो ब्रोंस्की

2

आग परियोजना पिछले 6 वर्षों में एक लंबा सफर तय किया। इसमें एक साधारण एपीआई है, जो पांडा सुविधाओं के एक उपयोगी सबसेट को कवर करता है।

dask.dataframe आंतरिक रूप से chunking का ख्याल रखता है, कई समानांतर संचालन का समर्थन करता है और आपको इन-मेमोरी ऑपरेशन के लिए आसानी से पंडों को वापस निर्यात करने की अनुमति देता है।

import dask.dataframe as dd

df = dd.read_csv('filename.csv')
df.head(10)  # return first 10 rows
df.tail(10)  # return last 10 rows

# iterate rows
for idx, row in df.iterrows():
    ...

# group by my_field and return mean
df.groupby(df.my_field).value.mean().compute()

# slice by column
df[df.my_field=='XYZ'].compute()

2

स्मृति समस्याओं के कारण के बिना किसी भी आकार की पाठ फ़ाइलों को लोड करने के लिए कोड का उपयोग करता है। यह गीगाबाइट आकार की फाइलों का समर्थन करता है

https://gist.github.com/iyvinjose/e6c1cb2821abd5f01fd1b9065cbc759d

फ़ाइल data_loading_utils.py डाउनलोड करें और इसे अपने कोड में आयात करें

प्रयोग

import data_loading_utils.py.py
file_name = 'file_name.ext'
CHUNK_SIZE = 1000000


def process_lines(data, eof, file_name):

    # check if end of file reached
    if not eof:
         # process data, data is one single line of the file

    else:
         # end of file reached

data_loading_utils.read_lines_from_file_as_data_chunks(file_name, chunk_size=CHUNK_SIZE, callback=self.process_lines)

process_lines विधि कॉलबैक फ़ंक्शन है। यह सभी लाइनों के लिए कहा जाएगा, जिसमें एक समय में फ़ाइल की एक एकल पंक्ति का प्रतिनिधित्व करने वाला पैरामीटर डेटा होता है।

आप अपने मशीन हार्डवेयर कॉन्फ़िगरेशन के आधार पर चर CHUNK_SIZE को कॉन्फ़िगर कर सकते हैं ।


जबकि मुझे यह तरीका पसंद है, आप अपने टेक्स्ट में लाइन के टूटने का जोखिम उठाते हैं। मैंने इसे व्यक्तिगत रूप से देखा, जिसका अर्थ है कि यदि आप फ़ाइल में sstring की खोज कर रहे हैं जैसे कि मैं था, तो मुझे कुछ याद होगा क्योंकि लाइन वे पर विखंडू में टूट गई थी। क्या इस से निकाल पाने के लिए कोई तरीका है? रीडलाइन का उपयोग करना अच्छी तरह से काम नहीं करता था क्योंकि मुझे
मिसकाउंट

0

इस बारे में कैसा है? अपनी फ़ाइल को विखंडू में विभाजित करें और फिर उसे पंक्ति द्वारा पढ़ें, क्योंकि जब आप कोई फ़ाइल पढ़ते हैं, तो आपका ऑपरेटिंग सिस्टम अगली पंक्ति को कैश कर देगा। यदि आप फ़ाइल लाइन को लाइन से पढ़ रहे हैं, तो आप कैश्ड जानकारी का कुशल उपयोग नहीं कर रहे हैं।

इसके बजाय, फ़ाइल को विखंडू में विभाजित करें और पूरे चंक को मेमोरी में लोड करें और फिर अपनी प्रोसेसिंग करें।

def chunks(file,size=1024):
    while 1:

        startat=fh.tell()
        print startat #file's object current position from the start
        fh.seek(size,1) #offset from current postion -->1
        data=fh.readline()
        yield startat,fh.tell()-startat #doesnt store whole list in memory
        if not data:
            break
if os.path.isfile(fname):
    try:
        fh=open(fname,'rb') 
    except IOError as e: #file --> permission denied
        print "I/O error({0}): {1}".format(e.errno, e.strerror)
    except Exception as e1: #handle other exceptions such as attribute errors
        print "Unexpected error: {0}".format(e1)
    for ele in chunks(fh):
        fh.seek(ele[0])#startat
        data=fh.read(ele[1])#endat
        print data

यह आशाजनक लग रहा है। क्या यह बाइट्स या लाइनों द्वारा लोड हो रहा है? मुझे लगता है कि अगर यह बाइट्स है तो लाइनों के टूटने का डर है .. हम एक बार में 1000 लाइनों को कैसे लोड कर सकते हैं और प्रक्रिया कर सकते हैं?
निखिल वीजे

0

धन्यवाद! मैंने हाल ही में अजगर 3 में परिवर्तित किया है और बड़ी फ़ाइलों को पढ़ने के लिए रीडलाइन (0) का उपयोग करके निराश किया है। इससे समस्या हल हो गई। लेकिन प्रत्येक पंक्ति को पाने के लिए, मुझे कुछ अतिरिक्त कदम उठाने पड़े। प्रत्येक पंक्ति एक "बी '" से पहले थी जो मुझे लगता है कि यह द्विआधारी प्रारूप में थी। "डिकोड (utf-8)" का उपयोग करके इसे एससीआई में बदल दिया।

तब मुझे प्रत्येक पंक्ति के बीच में एक "= \ n" निकालना था।

फिर मैंने नई लाइन पर लाइनों को विभाजित किया।

b_data=(fh.read(ele[1]))#endat This is one chunk of ascii data in binary format
        a_data=((binascii.b2a_qp(b_data)).decode('utf-8')) #Data chunk in 'split' ascii format
        data_chunk = (a_data.replace('=\n','').strip()) #Splitting characters removed
        data_list = data_chunk.split('\n')  #List containing lines in chunk
        #print(data_list,'\n')
        #time.sleep(1)
        for j in range(len(data_list)): #iterate through data_list to get each item 
            i += 1
            line_of_data = data_list[j]
            print(line_of_data)

यहाँ आरोही के कोड में "प्रिंट डेटा" के ठीक ऊपर शुरू होने वाला कोड है।


0

मैंने इस अन्य प्रश्न में एक समानांतर बाइट स्तर यादृच्छिक अभिगम दृष्टिकोण का प्रदर्शन किया:

रीडलाइन के बिना पाठ फ़ाइल में लाइनों की संख्या प्राप्त करना

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


0

सबसे अच्छा समाधान मुझे इस बारे में मिला, और मैंने इसे 330 एमबी फ़ाइल पर आज़माया।

lineno = 500
line_length = 8
with open('catfour.txt', 'r') as file:
    file.seek(lineno * (line_length + 2))
    print(file.readline(), end='')

जहाँ एक लाइन में वर्णों की संख्या line_length है। उदाहरण के लिए "abcd" में लाइन की लंबाई 4 है।

मैंने '\ n' वर्ण को छोड़ने और अगले वर्ण पर जाने के लिए 2 को लाइन की लंबाई में जोड़ा है।


-1

यह तब उपयोगी हो सकता है जब आप समानांतर में काम करना चाहते हैं और केवल आंकड़ों को पढ़ सकते हैं लेकिन इसे नई लाइनों के साथ साफ रखें।

def readInChunks(fileObj, chunkSize=1024):
    while True:
        data = fileObj.read(chunkSize)
        if not data:
            break
        while data[-1:] != '\n':
            data+=fileObj.read(1)
        yield data

-10
f=open('filename','r').read()
f1=f.split('\n')
for i in range (len(f1)):
    do_something_with(f1[i])

उम्मीद है की यह मदद करेगा।


5
क्या यह पूरी फ़ाइल को मेमोरी में नहीं पढ़ेगा? सवाल स्पष्ट रूप से पूछा जाता है कि कैसे बचें, इसलिए यह सवाल का जवाब नहीं देता है।
फर्मी विरोधाभास
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.