Ответ 1
Вы можете взглянуть на: Получить последние n строк файла с Python, похожие на хвост
Это действительно близко к тому, что вам нужно.
Мне нужно извлечь последнюю строку из нескольких очень больших (несколько сотен мегабайт) текстовых файлов, чтобы получить определенные данные. В настоящее время я использую python для циклического перехода по всем строкам до тех пор, пока файл не будет пустым, а затем обработаю последнюю возвращаемую строку, но я уверен, что есть более эффективный способ сделать это.
Каков наилучший способ получить только последнюю строку текстового файла с помощью python?
Вы можете взглянуть на: Получить последние n строк файла с Python, похожие на хвост
Это действительно близко к тому, что вам нужно.
Не прямой путь, но, вероятно, намного быстрее, чем простая реализация Python:
line = subprocess.check_output(['tail', '-1', filename])
Используйте метод seek
с отрицательным смещением и whence=os.SEEK_END
, чтобы прочитать блок с конца файла. Найдите этот блок для последнего символа конца строки и возьмите все символы после него. Если нет конца строки, вернитесь назад и повторите процесс.
def last_line(in_file, block_size=1024, ignore_ending_newline=False):
suffix = ""
in_file.seek(0, os.SEEK_END)
in_file_length = in_file.tell()
seek_offset = 0
while(-seek_offset < in_file_length):
# Read from end.
seek_offset -= block_size
if -seek_offset > in_file_length:
# Limit if we ran out of file (can't seek backward from start).
block_size -= -seek_offset - in_file_length
if block_size == 0:
break
seek_offset = -in_file_length
in_file.seek(seek_offset, os.SEEK_END)
buf = in_file.read(block_size)
# Search for line end.
if ignore_ending_newline and seek_offset == -block_size and buf[-1] == '\n':
buf = buf[:-1]
pos = buf.rfind('\n')
if pos != -1:
# Found line end.
return buf[pos+1:] + suffix
suffix = buf + suffix
# One-line file.
return suffix
Обратите внимание, что это не будет работать на вещах, которые не поддерживают seek
, например, stdin или сокеты. В этих случаях вы застряли, читая все это (например, команда tail
).
Если вы знаете максимальную длину строки, вы можете сделать
def getLastLine(fname, maxLineLength=80):
fp=file(fname, "rb")
fp.seek(-maxLineLength-1, 2) # 2 means "from the end of the file"
return fp.readlines()[-1]
Это работает на моем компьютере с Windows. Но я не знаю, что происходит на других платформах, если вы открываете текстовый файл в двоичном режиме. Бинарный режим необходим, если вы хотите использовать функцию seek().
Ищите конец файла минус 100 байт или около того. Прочитайте и найдите новую строку. Если здесь нет новой строки, запросите еще 100 байт или около того. Намочите, промойте, повторите. В конце концов вы найдете новую строку. Последняя строка начинается сразу после этой строки.
Лучший случайный сценарий: вы читаете только 100 байт.
Если вы можете выбрать разумную максимальную длину линии, вы можете искать почти конец файла перед началом чтения.
myfile.seek(-max_line_length, os.SEEK_END)
line = myfile.readlines()[-1]
with open('output.txt', 'r') as f:
lines = f.read().splitlines()
last_line = lines[-1]
print last_line
Не удалось загрузить файл в mmap, затем используйте mmap.rfind(string [, start [, end]]), чтобы найти второй последний символ EOL в файле? Поиск этой точки в файле должен указывать на последнюю строку, о которой я думаю.
Неэффективность здесь на самом деле не связана с Python, а с характером чтения файлов. Единственный способ найти последнюю строку - прочитать файл и найти окончания строки. Однако операцию поиска можно использовать для перехода к любому смещению байта в файле. Поэтому вы можете начинать очень близко к концу файла и захватывать большие и большие куски по мере необходимости до тех пор, пока не будет найден последний финал:
from os import SEEK_END
def get_last_line(file):
CHUNK_SIZE = 1024 # Would be good to make this the chunk size of the filesystem
last_line = ""
while True:
# We grab chunks from the end of the file towards the beginning until we
# get a new line
file.seek(-len(last_line) - CHUNK_SIZE, SEEK_END)
chunk = file.read(CHUNK_SIZE)
if not chunk:
# The whole file is one big line
return last_line
if not last_line and chunk.endswith('\n'):
# Ignore the trailing newline at the end of the file (but include it
# in the output).
last_line = '\n'
chunk = chunk[:-1]
nl_pos = chunk.rfind('\n')
# What being searched for will have to be modified if you are searching
# files with non-unix line endings.
last_line = chunk[nl_pos + 1:] + last_line
if nl_pos == -1:
# The whole chunk is part of the last line.
continue
return last_line
Здесь немного другое решение. Вместо многострочного, я сосредоточился только на последней строке, и вместо постоянного размера блока у меня есть динамический (удвоение) размер блока. См. Комментарии для получения дополнительной информации.
# Get last line of a text file using seek method. Works with non-constant block size.
# IDK if that speed things up, but it good enough for us,
# especially with constant line lengths in the file (provided by len_guess),
# in which case the block size doubling is not performed much if at all. Currently,
# we're using this on a textfile format with constant line lengths.
# Requires that the file is opened up in binary mode. No nonzero end-rel seeks in text mode.
REL_FILE_END = 2
def lastTextFileLine(file, len_guess=1):
file.seek(-1, REL_FILE_END) # 1 => go back to position 0; -1 => 1 char back from end of file
text = file.read(1)
tot_sz = 1 # store total size so we know where to seek to next rel file end
if text != b'\n': # if newline is the last character, we want the text right before it
file.seek(0, REL_FILE_END) # else, consider the text all the way at the end (after last newline)
tot_sz = 0
blocks = [] # For storing succesive search blocks, so that we don't end up searching in the already searched
j = file.tell() # j = end pos
not_done = True
block_sz = len_guess
while not_done:
if j < block_sz: # in case our block doubling takes us past the start of the file (here j also = length of file remainder)
block_sz = j
not_done = False
tot_sz += block_sz
file.seek(-tot_sz, REL_FILE_END) # Yes, seek() works with negative numbers for seeking backward from file end
text = file.read(block_sz)
i = text.rfind(b'\n')
if i != -1:
text = text[i+1:].join(reversed(blocks))
return str(text)
else:
blocks.append(text)
block_sz <<= 1 # double block size (converge with open ended binary search-like strategy)
j = j - block_sz # if this doesn't work, try using tmp j1 = file.tell() above
return str(b''.join(reversed(blocks))) # if newline was never found, return everything read
В идеале вы бы обернули это в класс LastTextFileLine и отслеживали скользящее среднее длины строк. Это может дать вам хороший len_guess.
lines = file.readlines()
fileHandle.close()
last_line = lines[-1]
Вам не нужно перебирать все строки. Можете ли вы просто прочитать файлы в списке и индексировать последнюю строку?
Я нашел этот пример:
http://www.daniweb.com/software-development/python/threads/121557
count = 0
f = open ('last_line1', 'r')
для строки в f.readlines():
line = line.strip()
count = count + 1
print line
количество распечаток
f.close()
count1 = 0
h = open ('last_line1', 'r')
для строки в h.readlines():
line = line.strip()
count1 = count1 + 1
if count1 == count:
print line #------------------------- this is the last line
h.close()