Bir dosyanın son satırını değiştirmek için değil, aynı tekniğin kullanıldığı benzer bir soruya cevabım üzerine yorum yapanların isteği üzerine bir cevap göndermek .
Önemli boyutta bir dosya mmap
için bunu yapmanın en iyi yolu budur. Mevcut mmap
cevabı iyileştirmek için , bu sürüm Windows ve Linux arasında taşınabilir ve daha hızlı çalışmalıdır (GB aralığında dosyalar bulunan 32 bit Python'da bazı değişiklikler yapılmadan çalışmasa da, bununla ilgili ipuçları için diğer cevaba bakınız. ve Python 2'de çalışacak şekilde değiştirmek için ).
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)
Bu, kuyruklanan satır sayısının, hepsini bir kerede güvenli bir şekilde belleğe okuyabileceğiniz kadar küçük olduğunu varsayar; ayrıca bunu bir jeneratör işlevi haline getirebilir ve son satırı aşağıdaki ile değiştirerek bir kerede manuel olarak okuyabilirsiniz:
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)
Son olarak, bu ikili modda (kullanımı gerekli mmap
) okunur, böylece str
satırlar (Py2) ve bytes
satırlar (Py3) verir; unicode
(Py2) veya str
(Py3) istiyorsanız , yinelemeli yaklaşım sizin için kod çözme ve / veya yeni satırları düzeltme için ayarlanabilir:
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
Not: Bunu test etmek için Python'a erişemediğim bir makineye yazdım. Bir şey yazıp yazmadığımı lütfen bize bildirin; Buna benzer yeterince oldu benim diğer yanıt Bunu düşünmek onu çalışmalıdır, ancak ince ayarlar (örneğin bir taşıma offset
) ince hatalara yol açabilir. Herhangi bir hata varsa lütfen yorumlarda bana bildirin.
seek(0,2)
sonratell()
) almak ve başlangıca göre aramak için bu değeri kullanmak için değiştirdik.