Каков наиболее эффективный способ получить первую и последнюю строку текстового файла?
У меня есть текстовый файл, который содержит метку времени в каждой строке. Моя цель-найти временной диапазон. Все времена в порядке, поэтому первая строка будет самым ранним временем, а последняя строка будет последним временем. Мне нужна только самая первая и самая последняя строчка. Каков был бы наиболее эффективный способ получить эти строки в python?
примечание: эти файлы относительно большой длины, около 1-2 миллионов строк каждый, и я должен сделать это для нескольких сотен файлов.
12 ответов:
with open(fname, 'rb') as fh: first = next(fh).decode() fh.seek(-1024, 2) last = fh.readlines()[-1].decode()
значение переменной здесь 1024: оно представляет собой среднюю длину строки. Я выбираю 1024 только для примера. Если у вас есть оценка средней длины линии, вы можете просто использовать это значение раз в 2.
поскольку вы понятия не имеете о возможной верхней границе длины строки, очевидным решением было бы зациклить файл:
for line in fh: pass last = line
вам не нужно беспокоиться о двоичный флаг вам можно просто использовать
open(fname)
.ETA: поскольку у вас есть много файлов для работы, вы можете создать образец из нескольких десятков файлов с помощью
random.sample
и запустить этот код на них, чтобы определить длину последней строки. С априорно большим значением сдвига позиции (скажем, 1 МБ). Это поможет вам оценить значение для полного выполнения.
вы можете открыть файл для чтения и прочитать первую строку с помощью встроенного
readline()
, затем найдите конец файла и отступите назад, пока не найдете предыдущую строку Эол и прочитал последнюю строку оттуда.with open(file, "rb") as f: first = f.readline() # Read the first line. f.seek(-2, os.SEEK_END) # Jump to the second last byte. while f.read(1) != b"\n": # Until EOL is found... f.seek(-2, os.SEEK_CUR) # ...jump back the read byte plus one more. last = f.readline() # Read last line.
переход ко второму последнему байту вместо последнего предотвращает возврат непосредственно из-за конечного EOL. В то время как вы отступаете назад, вы также захотите сделать шаг на два байта с момента чтения и проверка на Эол толкает положение вперед на один шаг.
при использовании
seek
форматfseek(offset, whence=0)
здесьwhence
означает то, что смещение относительно. Цитата из docs.python.org:
SEEK_SET
или0
= искать с начала потока (по умолчанию); смещение должно быть либо числом, возвращаемым TextIOBase.скажи (), или ноль. Любое другое значение смещения производит неопределенное поведение.SEEK_CUR
или1
= "искать" в текущей позиции; смещение должно быть равно нулю ,что не является операцией (все остальные значения неподдерживаемый.)SEEK_END
или2
= перейти к конец потока; смещение должно быть равно нулю (все остальные значения не поддерживаются).запуск его через timeit 10k раз в файле с 6K строк на общую сумму 200kB дал мне 1.62 s против 6.92 s при сравнении с for-loop ниже, что было предложено ранее. Использование файла размером 1,3 ГБ, все еще с 6K строками, сто раз привело к 8,93 против 86,95.
with open(file, "rb") as f: first = f.readline() # Read the first line. for last in f: pass # Loop through the whole file reading it all.
вот модифицированная версия ответа SilentGhost, которая будет делать то, что вы хотите.
with open(fname, 'rb') as fh: first = next(fh) offs = -100 while True: fh.seek(offs, 2) lines = fh.readlines() if len(lines)>1: last = lines[-1] break offs *= 2 print first print last
нет необходимости в верхней границе для длины линии здесь.
вы можете использовать команды Unix? Я думаю, что с помощью
head -1
иtail -n 1
вероятно, наиболее эффективные методы. Кроме того, вы можете использовать простойfid.readline()
чтобы получить первую строку иfid.readlines()[-1]
, но это может занять слишком много памяти.
Это мое решение, совместимое также с Python3. Он также управляет пограничными случаями, но он пропускает поддержку utf-16:
def tail(filepath): """ @author Marco Sulla (marcosullaroma@gmail.com) @date May 31, 2016 """ try: filepath.is_file fp = str(filepath) except AttributeError: fp = filepath with open(fp, "rb") as f: size = os.stat(fp).st_size start_pos = 0 if size - 1 < 0 else size - 1 if start_pos != 0: f.seek(start_pos) char = f.read(1) if char == b"\n": start_pos -= 1 f.seek(start_pos) if start_pos == 0: f.seek(start_pos) else: char = "" for pos in range(start_pos, -1, -1): f.seek(pos) char = f.read(1) if char == b"\n": break return f.readline()
это ispired по Трасп это!--5--> и комментарий другого Паркера.
сначала открыть файл в режиме чтения.Затем используйте метод readlines () для чтения строки за строкой.Все строки хранятся в списке.Теперь вы можете использовать срезы списка, чтобы получить первую и последнюю строки файла.
a=open('file.txt','rb') lines = a.readlines() if lines: first_line = lines[:1] last_line = lines[-1]
w=open(file.txt, 'r') print ('first line is : ',w.readline()) for line in w: x= line print ('last line is : ',x) w.close()
The
for
цикл проходит через линии иx
получает последнюю строку на последней итерации.
with open("myfile.txt") as f: lines = f.readlines() first_row = lines[0] print first_row last_row = lines[-1] print last_row
вот расширение ответа @Trasp, которое имеет дополнительную логику для обработки углового случая файла, который имеет только одну строку. Это может быть полезно в этом случае, если вы неоднократно хотите прочитать последнюю строку в файл, который постоянно обновляется. Без этого, если вы попытаетесь захватить последнюю строку файла, который только что был создан и имеет только одну строку,
IOError: [Errno 22] Invalid argument
будет поднят.def tail(filepath): with open(filepath, "rb") as f: first = f.readline() # Read the first line. f.seek(-2, 2) # Jump to the second last byte. while f.read(1) != b"\n": # Until EOL is found... try: f.seek(-2, 1) # ...jump back the read byte plus one more. except IOError: f.seek(-1, 1) if f.tell() == 0: break last = f.readline() # Read last line. return last
никто не упоминал, используя обратный:
f=open(file,"r") r=reversed(f.readlines()) last_line_of_file = r.next()
получение первой строки тривиально легко. Для последней строки, предполагая, что вы знаете приблизительную верхнюю границу длины строки,ОС.lseek какая-то сумма из
SEEK_END
найти предпоследнее окончание строки, а затем readline () последняя строка.
with open(filename, "r") as f: first = f.readline() if f.read(1) == '': return first f.seek(-2, 2) # Jump to the second last byte. while f.read(1) != b"\n": # Until EOL is found... f.seek(-2, 1) # ...jump back the read byte plus one more. last = f.readline() # Read last line. return last
приведенный выше ответ является модифицированной версией приведенных выше ответов, которая обрабатывает случай, когда в файле есть только одна строка