Каков наиболее эффективный способ получить первую и последнюю строку текстового файла?


У меня есть текстовый файл, который содержит метку времени в каждой строке. Моя цель-найти временной диапазон. Все времена в порядке, поэтому первая строка будет самым ранним временем, а последняя строка будет последним временем. Мне нужна только самая первая и самая последняя строчка. Каков был бы наиболее эффективный способ получить эти строки в python?

примечание: эти файлы относительно большой длины, около 1-2 миллионов строк каждый, и я должен сделать это для нескольких сотен файлов.

12 52

12 ответов:

docs for IO module

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

приведенный выше ответ является модифицированной версией приведенных выше ответов, которая обрабатывает случай, когда в файле есть только одна строка