एक विशाल पाठ फ़ाइल में किसी विशेष पंक्ति में कैसे कूदें?


107

क्या नीचे दिए गए कोड का कोई विकल्प है:

startFromLine = 141978 # or whatever line I need to jump to

urlsfile = open(filename, "rb", 0)

linesCounter = 1

for line in urlsfile:
    if linesCounter > startFromLine:
        DoSomethingWithThisLine(line)

    linesCounter += 1

अगर मैं (~15MB)अज्ञात लेकिन अलग-अलग लंबाई की लाइनों के साथ एक विशाल पाठ फ़ाइल को संसाधित कर रहा हूं , और एक विशेष पंक्ति में कूदने की आवश्यकता है, जिसे मैं पहले से जानता हूं? मैं उन्हें एक-एक करके संसाधित करने में बुरा महसूस करता हूं जब मुझे पता है कि मैं कम से कम पहली छमाही में फाइल को अनदेखा कर सकता हूं। यदि कोई हो तो अधिक सुरुचिपूर्ण समाधान की तलाश करना।


आपको कैसे पता चलेगा कि फ़ाइल का पहला 1/2 "\ n" s का एक गुच्छा नहीं है जबकि दूसरी छमाही एकल पंक्ति है? आपको इस बारे में बुरा क्यों लगता है?
एंड्रयू डल्के

7
मुझे लगता है कि शीर्षक भ्रामक है - tbh 15MB वास्तव में "विशाल पाठ फ़ाइल" नहीं है, कम से कम कहने के लिए ...
Pms

जवाबों:


30

अलसी :

linecacheमॉड्यूल, आंतरिक रूप से अनुकूलन करने के लिए प्रयास कर रहा एक कैश, मामले ऐसे कई लाइनों किसी एक फ़ाइल से पाठ किया जाता है का उपयोग करते हुए, एक अजगर स्रोत फ़ाइल से किसी भी लाइन प्राप्त करने के लिए एक की अनुमति देता है। इसका उपयोग tracebackमॉड्यूल द्वारा स्वरूपित ट्रेसबैक में शामिल करने के लिए स्रोत लाइनों को पुनः प्राप्त करने के लिए किया जाता है ...


164
मैंने इस मॉड्यूल के स्रोत कोड की जाँच की: पूरी फाइल को मेमोरी में पढ़ा जाता है! इसलिए मैं निश्चित रूप से किसी फ़ाइल में दिए गए लाइन को जल्दी से एक्सेस करने के उद्देश्य से इस उत्तर को समाप्त कर दूंगा।
21.115 बजे मिनीक्वार मार्क

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

5
आपके OS का वर्चुअल मेमोरी मैनेजर काफी हद तक मदद करता है, इसलिए मेमोरी में बड़ी फ़ाइलों को पढ़ना धीमा नहीं हो सकता है यदि आप बहुत सारे पेज दोष उत्पन्न नहीं कर रहे हैं :) इसके विपरीत, इसे "बेवकूफ तरीका" और बहुत सारे और बहुत सारे आवंटित करना याददाश्त तेज हो सकती है। मैंने इस पर मुल्तानी
मोर्टन जेन्सेन

13
100 जी फ़ाइल की कोशिश करो, यह बेकार है। मुझे f.tell (), f.seek (), f.readline ()
whi

114

आप कम से कम एक बार फाइल में पढ़े बिना आगे नहीं बढ़ सकते, क्योंकि आपको नहीं पता कि लाइन ब्रेक कहां हैं। आप कुछ ऐसा कर सकते हैं:

# Read in the file once and build a list of line offsets
line_offset = []
offset = 0
for line in file:
    line_offset.append(offset)
    offset += len(line)
file.seek(0)

# Now, to skip to line n (with the first line being line 0), just do
file.seek(line_offset[n])

2
+1, लेकिन सावधान रहें कि यह केवल तभी उपयोगी है जब वह कई यादृच्छिक लाइनों पर कूदने वाला हो! लेकिन अगर वह सिर्फ एक ही लाइन पर कूद रहा है, तो यह बेकार है
Hasen

3
+1: इसके अलावा, यदि फ़ाइल नहीं बदलती है, तो लाइन नंबर इंडेक्स को उठाया जा सकता है और पुन: उपयोग किया जा सकता है, जिससे फ़ाइल को स्कैन करने की प्रारंभिक लागत को आगे बढ़ाया जा सकता है।
एस.लॉट

ठीक है, जब मैंने वहां छलांग लगाई तो मैं इस स्थिति से लाइन-दर-पंक्ति कैसे शुरू करूंगा?
user63503

8
ध्यान देने वाली एक बात (विशेष रूप से विंडोज़ पर): बाइनरी मोड में फ़ाइल खोलने के लिए सावधान रहें, या ऑफसेट = file.tell () का उपयोग करें। खिड़कियों पर पाठ मोड में, लाइन बाइट की तुलना में कम होगी जो कि डिस्क पर कच्ची लंबाई है (\ r \ n द्वारा प्रतिस्थापित \ n)
ब्रायन

2
@photographer: रीड () या रीडलाइन () का उपयोग करें, वे वर्तमान स्थिति से शुरू करते हैं जैसा कि तलाश के द्वारा निर्धारित किया गया है।
२.२ S

22

यदि रेखाएँ अलग-अलग लंबाई की हैं, तो आपके पास वास्तव में बहुत सारे विकल्प नहीं हैं ... आपको अगली पंक्ति में आगे बढ़ने पर यह जानने के लिए दुख की बात है कि पंक्ति को समाप्त करने की आवश्यकता है।

हालाँकि, आप नाटकीय रूप से इसे गति दे सकते हैं और अंतिम पैरामीटर को "ओपन" में बदलकर कुछ नहीं 0 तक स्मृति उपयोग को कम कर सकते हैं।

0 का मतलब है कि फ़ाइल रीडिंग ऑपरेशन अप्रभावित है, जो बहुत धीमा और डिस्क गहन है। 1 का मतलब है कि फ़ाइल लाइन बफ़र्ड है, जो एक सुधार होगा। 1 से ऊपर कुछ भी (8k कहो .. यानी: 8096, या उच्चतर) मेमोरी में फ़ाइल का हिस्सा पढ़ता है। आप अभी भी इसके माध्यम से एक्सेस करते हैं for line in open(etc):, लेकिन अजगर केवल एक समय में थोड़ा सा जाता है, इसके प्रसंस्करण के बाद प्रत्येक बफ़र्ड चंक को त्याग देता है।


6
8K 8192 है, सुरक्षित पक्ष पर रहने के लिए 8 << 10 लिखना बेहतर है। :)
खोलना

क्या आपको पता है कि बफ़र्स को बाइट्स पर निर्दिष्ट किया गया है? उपयुक्त प्रारूप क्या हैं? क्या मैं '8k' लिख सकता था? या यह it 8096 ’होना चाहिए?
user63503

1
HAHAHA ... फ्राइडे होना चाहिए ... मैं स्पष्ट रूप से गणित नहीं कर सकता। बफ़र का आकार वास्तव में एक पूर्णांक है जो बाइट्स व्यक्त करता है, इसलिए 8192 लिखें (8096 :-) नहीं), 8 के बजाय
जरेट हार्डी

मेरी खुशी - आशा है कि यह काम करता है। एक आधुनिक प्रणाली पर, आप संभवतः बफर आकार को काफी बढ़ा सकते हैं। 8k मेरी याददाश्त में एक कारण है कि किसी कारण से मैं पहचान नहीं सकता।
जरेट हार्डी

मैंने यहाँ कुछ परीक्षण किया है, और इसे -1 पर सेट किया है (ओएस डिफ़ॉल्ट, अक्सर 8k, लेकिन अक्सर बताने में मुश्किल होता है), यह जितना जल्दी हो सके उतना तेज़ लगता है। उस ने कहा, इसका एक हिस्सा यह हो सकता है कि मैं एक आभासी सर्वर पर परीक्षण कर रहा हूं।
ऑस्कर स्मिथ

12

मैं शायद प्रचुर मात्रा में राम द्वारा खराब हो गया हूं, लेकिन 15 एम बहुत बड़ा नहीं है। के साथ स्मृति में पढ़ना readlines() जो मैं आमतौर पर इस आकार की फाइलों के साथ करता हूं। उसके बाद एक लाइन एक्सेस करना तुच्छ है।


मैं पूरी फाइल पढ़ने में थोड़ा झिझक क्यों रहा था - हो सकता है कि उनमें से कई प्रक्रियाएं चल रही हों, और अगर उन दर्जनों लोगों ने 12 एमबी 15 एमबी की प्रत्येक फाइल को पढ़ा तो यह अच्छा नहीं हो सकता है। लेकिन मुझे यह पता लगाने के लिए परीक्षण करने की आवश्यकता है कि क्या यह काम करेगा। धन्यवाद।
user63503

4
Hrm, और अगर यह 1GB फ़ाइल है तो क्या होगा?
नूह

@photographer: यहां तक ​​कि 15MB फ़ाइलों में पढ़ने वाली "कई" प्रक्रियाएं एक विशिष्ट आधुनिक मशीन पर निर्भर नहीं होनी चाहिए (निश्चित रूप से, वास्तव में आप उनके साथ क्या कर रहे हैं)।
जैकब गेब्रियलसन

जैकब, हां, मुझे बस कोशिश करनी चाहिए। यदि vm क्रैश नहीं हुआ है तो प्रक्रिया (es) एक वर्चुअल मशीन पर हफ्तों से चल रही है। दुर्भाग्य से पिछली बार यह 6 दिनों के बाद दुर्घटनाग्रस्त हो गया था। मुझे वहां से जारी रखने की आवश्यकता है जहां यह अचानक बंद हो गया। अभी भी यह पता लगाने की जरूरत है कि यह कैसे छोड़ा गया था।
user63503

@ नोहा: लेकिन यह नहीं है! आप आगे क्यों नहीं जाते? क्या होगा अगर फ़ाइल 128TB? कई OS इसके समर्थन में सक्षम नहीं होंगे। वे आते ही समस्या का समाधान क्यों नहीं करते?
साइलेंटगॉस्ट

7

मैं किसी से भी पृथक नहीं हूं

line = next(itertools.islice(Fhandle,index_of_interest,index_of_interest+1),None) # just the one line

या यदि आप संपूर्ण फ़ाइल चाहते हैं

rest_of_file = itertools.islice(Fhandle,index_of_interest)
for line in rest_of_file:
    print line

या यदि आप फ़ाइल से हर दूसरी पंक्ति चाहते हैं

rest_of_file = itertools.islice(Fhandle,index_of_interest,None,2)
for odd_line in rest_of_file:
    print odd_line

5

चूँकि बिना पढ़े ही सभी पंक्तियों की लीनता को निर्धारित करने का कोई तरीका नहीं है, आपके पास अपनी आरंभिक पंक्ति से पहले सभी पंक्तियों पर पुनरावृति करने के अलावा कोई विकल्प नहीं है। आप बस इतना कर सकते हैं कि यह अच्छा दिखे। यदि फ़ाइल वास्तव में बहुत बड़ी है तो आप जनरेटर आधारित दृष्टिकोण का उपयोग करना चाहते हैं:

from itertools import dropwhile

def iterate_from_line(f, start_from_line):
    return (l for i, l in dropwhile(lambda x: x[0] < start_from_line, enumerate(f)))

for line in iterate_from_line(open(filename, "r", 0), 141978):
    DoSomethingWithThisLine(line)

नोट: इस दृष्टिकोण में सूचकांक शून्य है।


4

यदि आप पूरी फ़ाइल को स्मृति में नहीं पढ़ना चाहते हैं .. तो आपको सादे पाठ के अलावा कुछ प्रारूप के साथ आने की आवश्यकता हो सकती है।

बेशक यह सब इस बात पर निर्भर करता है कि आप क्या करने की कोशिश कर रहे हैं, और आप कितनी बार फ़ाइल में कूदेंगे।

उदाहरण के लिए, यदि आप एक ही फ़ाइल में कई बार लाइनों पर जा रहे हैं , और आप जानते हैं कि फ़ाइल इसके साथ काम करते समय नहीं बदलती है, तो आप ऐसा कर सकते हैं:
सबसे पहले, पूरी फ़ाइल से गुजरें, और रिकॉर्ड करें " कुछ प्रमुख लाइन-संख्याओं (जैसे, कभी 1000 लाइनें) की तलाश-स्थिति ",
फिर यदि आप 12005 लाइन चाहते हैं , तो 12000 की स्थिति पर जाएं (जो आपने रिकॉर्ड किया है) फिर 5 लाइनें पढ़ें और आपको पता चल जाएगा '12005 और इतने पर फिर से


3

यदि आप फ़ाइल में स्थिति पहले से जानते हैं (बल्कि लाइन नंबर), तो आप उस स्थिति में जाने के लिए file.seek () का उपयोग कर सकते हैं ।

संपादित करें : आप linecache.getline (फ़ाइल नाम, लिनीनो) फ़ंक्शन का उपयोग कर सकते हैं , जो लाइन लाइनो की सामग्री को वापस कर देगा, लेकिन पूरी फ़ाइल को मेमोरी में पढ़ने के बाद ही। यदि आप फ़ाइल के भीतर से अनियमित रूप से पंक्तियों तक पहुँच रहे हैं तो अच्छा है (जैसा कि अजगर खुद ट्रेसबैक प्रिंट करना चाहते हैं) लेकिन 15MB फ़ाइल के लिए अच्छा नहीं है।


मैं निश्चित रूप से इस उद्देश्य के लिए लिनेशे का उपयोग नहीं करूंगा, क्योंकि यह अनुरोधित लाइन को वापस करने से पहले पूरी फ़ाइल को मेमोरी में पढ़ता है।
21.116 को मिनीक्वार

हाँ, यह सच होने के लिए बहुत अच्छा लग रहा था। मैं अभी भी चाहता हूं कि यह कुशलतापूर्वक करने के लिए एक मॉड्यूल था, लेकिन इसके बजाय file.seek () पद्धति का उपयोग करें।
नूह

3

वह फ़ाइल उत्पन्न करता है जिसे आप संसाधित करना चाहते हैं? यदि यह आपके नियंत्रण में कुछ है, तो आप उस समय एक सूचकांक उत्पन्न कर सकते हैं (जो लाइन किस स्थिति में है।) जिस समय फ़ाइल को जोड़ा जाता है। इंडेक्स फाइल फिक्स्ड लाइन साइज (स्पेस पेड या 0 पेड नंबर) की हो सकती है और निश्चित रूप से छोटी होगी। और इस प्रकार qucikly को पढ़ा और संसाधित किया जा सकता है।

  • आपको कौन सी लाइन चाहिए?
  • इंडेक्स फाइल में संबंधित लाइन नंबर की बाइट ऑफसेट की गणना करें (संभव है क्योंकि इंडेक्स फाइल का लाइन आकार स्थिर है)।
  • इंडेक्स फ़ाइल से लाइन प्राप्त करने के लिए सीक या जो भी सीधे कूदने का उपयोग करें।
  • वास्तविक फ़ाइल की संबंधित पंक्ति के लिए बाइट ऑफ़सेट प्राप्त करने के लिए पार्स।

3

मुझे एक ही समस्या थी (बड़ी फ़ाइल विशिष्ट पंक्ति से पुनर्प्राप्त करने की आवश्यकता)।

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

मुझे अगले निर्णय का पता चला: सबसे पहले मैंने प्रत्येक पंक्ति की शुरुआत की स्थिति के साथ शब्दकोश को पूरा किया (कुंजी लाइन संख्या है, और मूल्य - पिछली लाइनों की कम लंबाई)।

t = open(file,’r’)
dict_pos = {}

kolvo = 0
length = 0
for each in t:
    dict_pos[kolvo] = length
    length = length+len(each)
    kolvo = kolvo+1

अंत में, लक्ष्य समारोह:

def give_line(line_number):
    t.seek(dict_pos.get(line_number))
    line = t.readline()
    return line

t.seek (line_number) - कमांड जो लाइन इंसेप्शन तक फाइल के प्रूनिंग को निष्पादित करती है। इसलिए, यदि आप अगली बार रीडलाइन देते हैं - तो आप अपनी लक्ष्य रेखा प्राप्त करते हैं।

इस तरह के दृष्टिकोण का उपयोग करके मैंने समय का महत्वपूर्ण हिस्सा बचाया है।


3

लाइनों की भरपाई खोजने के लिए आप mmap का उपयोग कर सकते हैं। MMap किसी फ़ाइल को प्रोसेस करने का सबसे तेज़ तरीका है

उदाहरण:

with open('input_file', "r+b") as f:
    mapped = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
    i = 1
    for line in iter(mapped.readline, ""):
        if i == Line_I_want_to_jump:
            offsets = mapped.tell()
        i+=1

इसके बाद f.seek (ऑफसेट) का उपयोग उस लाइन पर जाएं, जिसकी आपको जरूरत है


2

क्या लाइनों में स्वयं कोई सूचकांक जानकारी है? यदि प्रत्येक पंक्ति की सामग्री कुछ " <line index>:Data" जैसी थी , तो seek()दृष्टिकोण का उपयोग फ़ाइल के माध्यम से एक द्विआधारी खोज करने के लिए किया जा सकता है, भले ही वह राशि Dataपरिवर्तनशील हो। आप फ़ाइल के मध्य बिंदु की तलाश करेंगे, एक पंक्ति पढ़ेंगे, जांचें कि क्या इसका सूचकांक उच्चतर है या आप जो चाहते हैं उससे कम है, आदि।

अन्यथा, सबसे अच्छा आप कर सकते हैं बस है readlines()। यदि आप सभी 15MB पढ़ना नहीं चाहते हैं, तो आप sizehintकम से कम बहुत readline()से कॉल को बदलने के लिए तर्क का उपयोग कर सकते हैं readlines()


2

यदि आप एक टेक्स्ट फ़ाइल के साथ काम कर रहे हैं और लिनक्स सिस्टम पर आधारित है , तो आप लिनक्स कमांड का उपयोग कर सकते हैं।
मेरे लिए, यह अच्छी तरह से काम किया!

import commands

def read_line(path, line=1):
    return commands.getoutput('head -%s %s | tail -1' % (line, path))

line_to_jump = 141978
read_line("path_to_large_text_file", line_to_jump)

बेशक यह खिड़कियों या किसी प्रकार के लिनेक्स के गोले के साथ संगत नहीं है जो सिर / पूंछ का समर्थन नहीं करते हैं।
विज्मन

क्या यह पायथन में करने से ज्यादा तेज है?
शामून

क्या इससे कई लाइनें मिल सकती हैं?
शमून

1

एक बार में लाइनों का एक हिस्सा पढ़ने के लिए 'readlines (sizehint)' का उपयोग करके एक उदाहरण दिया गया है। डीएनएस ने कहा कि समाधान। मैंने इस उदाहरण को लिखा क्योंकि यहाँ अन्य उदाहरण एकल-रेखा उन्मुख हैं।

def getlineno(filename, lineno):
    if lineno < 1:
        raise TypeError("First line is line 1")
    f = open(filename)
    lines_read = 0
    while 1:
        lines = f.readlines(100000)
        if not lines:
            return None
        if lines_read + len(lines) >= lineno:
            return lines[lineno-lines_read-1]
        lines_read += len(lines)

print getlineno("nci_09425001_09450000.smi", 12000)

0

कोई भी उत्तर विशेष रूप से संतोषजनक नहीं है, इसलिए यहां सहायता के लिए एक छोटा सा स्निपेट है।

class LineSeekableFile:
    def __init__(self, seekable):
        self.fin = seekable
        self.line_map = list() # Map from line index -> file position.
        self.line_map.append(0)
        while seekable.readline():
            self.line_map.append(seekable.tell())

    def __getitem__(self, index):
        # NOTE: This assumes that you're not reading the file sequentially.  
        # For that, just use 'for line in file'.
        self.fin.seek(self.line_map[index])
        return self.fin.readline()

उदाहरण का उपयोग:

In: !cat /tmp/test.txt

Out:
Line zero.
Line one!

Line three.
End of file, line four.

In:
with open("/tmp/test.txt", 'rt') as fin:
    seeker = LineSeekableFile(fin)    
    print(seeker[1])
Out:
Line one!

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

मैं उपयोगकर्ता के विवेक पर एमआईटी या अपाचे लाइसेंस के तहत ऊपर स्निपेट प्रदान करता हूं।


-1

इस फ़ंक्शन का उपयोग लाइन n पर लौटने के लिए कर सकते हैं:

def skipton(infile, n):
    with open(infile,'r') as fi:
        for i in range(n-1):
            fi.next()
        return fi.next()

यह तर्क काम नहीं करता है अगर लगातार खाली लाइनें होती हैं, तो Fi.next () एक बार में सभी खाली लाइनों को छोड़ देता है, अन्यथा यह अच्छा है :)
अनवेश यलमार्थी

ओपी का उल्लेख नहीं है कि लाइनों में गैर-मानक लाइन-ब्रेक के साथ लाइनें हैं। उस स्थिति में, आपको आंशिक पंक्ति विराम के लिए कम से कम एक-कथन के साथ प्रत्येक पंक्ति को पार्स करना होगा।
ksed
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.