मेरे जवाब पर टिप्पणी करने वालों के इशारे पर एक ऐसे ही सवाल पर जवाब देना जहां एक ही तकनीक का इस्तेमाल किसी फ़ाइल की अंतिम पंक्ति को म्यूट करने के लिए किया गया था, न कि इसे पाने के लिए।
महत्वपूर्ण आकार की फ़ाइल के लिए, mmap
ऐसा करने का सबसे अच्छा तरीका है। मौजूदा mmap
उत्तर पर सुधार करने के लिए , यह संस्करण विंडोज़ और लिनक्स के बीच पोर्टेबल है, और तेज़ी से चलना चाहिए (हालांकि यह जीबी रेंज में फ़ाइलों के साथ 32 बिट पायथन पर कुछ संशोधनों के बिना काम नहीं करेगा, इसे संभालने पर संकेत के लिए अन्य उत्तर देखें , और पायथन 2 पर काम करने के लिए संशोधन के लिए )।
import io # Gets consistent version of open for both Py2.7 and Py3.x
import itertools
import mmap
def skip_back_lines(mm, numlines, startidx):
'''Factored out to simplify handling of n and offset'''
for _ in itertools.repeat(None, numlines):
startidx = mm.rfind(b'\n', 0, startidx)
if startidx < 0:
break
return startidx
def tail(f, n, offset=0):
# Reopen file in binary mode
with io.open(f.name, 'rb') as binf, mmap.mmap(binf.fileno(), 0, access=mmap.ACCESS_READ) as mm:
# len(mm) - 1 handles files ending w/newline by getting the prior line
startofline = skip_back_lines(mm, offset, len(mm) - 1)
if startofline < 0:
return [] # Offset lines consumed whole file, nothing to return
# If using a generator function (yield-ing, see below),
# this should be a plain return, no empty list
endoflines = startofline + 1 # Slice end to omit offset lines
# Find start of lines to capture (add 1 to move from newline to beginning of following line)
startofline = skip_back_lines(mm, n, startofline) + 1
# Passing True to splitlines makes it return the list of lines without
# removing the trailing newline (if any), so list mimics f.readlines()
return mm[startofline:endoflines].splitlines(True)
# If Windows style \r\n newlines need to be normalized to \n, and input
# is ASCII compatible, can normalize newlines with:
# return mm[startofline:endoflines].replace(os.linesep.encode('ascii'), b'\n').splitlines(True)
यह माना जाता है कि लाइनों की संख्या काफी छोटी है आप सुरक्षित रूप से उन सभी को एक बार में मेमोरी में पढ़ सकते हैं; आप इसे जनरेटर फ़ंक्शन भी बना सकते हैं और अंतिम पंक्ति को प्रतिस्थापित करके एक बार में मैन्युअल रूप से एक पंक्ति पढ़ सकते हैं:
mm.seek(startofline)
# Call mm.readline n times, or until EOF, whichever comes first
# Python 3.2 and earlier:
for line in itertools.islice(iter(mm.readline, b''), n):
yield line
# 3.3+:
yield from itertools.islice(iter(mm.readline, b''), n)
अंत में, यह बाइनरी मोड में पढ़ा जाता है (उपयोग करने के लिए आवश्यक mmap
) इसलिए यह str
लाइनें (Py2) और bytes
लाइनें (Py3) देता है; यदि आप unicode
(Py2) या str
(Py3) चाहते हैं, तो पुनरावृत्त दृष्टिकोण को आपके लिए डिकोड करने और / या सुर्खियों को ठीक करने के लिए घुमाया जा सकता है:
lines = itertools.islice(iter(mm.readline, b''), n)
if f.encoding: # Decode if the passed file was opened with a specific encoding
lines = (line.decode(f.encoding) for line in lines)
if 'b' not in f.mode: # Fix line breaks if passed file opened in text mode
lines = (line.replace(os.linesep, '\n') for line in lines)
# Python 3.2 and earlier:
for line in lines:
yield line
# 3.3+:
yield from lines
नोट: मैंने यह सब एक मशीन पर टाइप किया जहां मुझे परीक्षण करने के लिए पायथन की पहुंच की कमी है। कृपया मुझे बताएं कि क्या मैंने कुछ टाइप किया है; यह मेरे अन्य उत्तर के समान था कि मुझे लगता है कि इसे काम करना चाहिए, लेकिन ट्वीक्स (जैसे हैंडलिंग offset
) सूक्ष्म त्रुटियों को जन्म दे सकता है। अगर कोई ग़लती हो तो कृपया मुझे टिप्पणियों में बताएं।
seek(0,2)
तबtell()
) प्राप्त करने के लिए इसे संशोधित किया है , और शुरुआत के सापेक्ष उस मूल्य का उपयोग करें।