Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

docs для io-модуля

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 МБ). Это поможет вам оценить значение для полного прогона.

Ответ 2

Вы можете открыть файл для чтения и прочитать первую строку, используя встроенный readline(), затем найдите конец файла и шаг назад, пока не найдете строку, предшествующую EOL и прочитайте последнюю строку оттуда.

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. В то время как вы отступаете назад, вам также нужно выполнить два байта, так как чтение и проверка EOL подталкивает позицию вперед на один шаг.

При использовании seek формат fseek(offset, whence=0) где whence означает, к чему относится смещение. Цитата из docs.python.org:

  • SEEK_SET или 0= искать с начала потока (по умолчанию); смещение должно быть либо числом, возвращаемым TextIOBase.tell() или ноль. Любое другое значение смещения дает undefined.
  • SEEK_CUR или 1= "искать" текущую позицию; смещение должно быть нулевым, что является нерабочим (все остальные значения не поддерживается).
  • SEEK_END или 2= искать до конца потока; offset должно быть равно нулю (все остальные значения не поддерживаются).

Запуск его через timeit 10k раз в файле с 6k строками на 200kB дал мне 1.62s против 6.92s по сравнению с for-loop ниже, что было предложено ранее. Используя файл размером 1,3 ГБ, все еще с 6-килограммовыми линиями, сто раз приводил к 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.

Ответ 3

Здесь приведена модифицированная версия ответа 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

Здесь нет необходимости в верхней границе длины строки.

Ответ 4

Можете ли вы использовать команды unix? Я думаю, что использование head -1 и tail -n 1, вероятно, является наиболее эффективным методом. В качестве альтернативы вы можете использовать простой fid.readline() для получения первой строки и fid.readlines()[-1], но это может занять слишком много памяти.

Ответ 5

Сначала откройте файл в режиме чтения. Затем используйте метод readlines() для чтения строки за строкой. Все строки, хранящиеся в списке. Теперь вы можете использовать куски списка, чтобы получить первую и последнюю строки файла.

    a=open('file.txt','rb')
    lines = a.readlines()
    if lines:
        first_line = lines[:1]
        last_line = lines[-1]

Ответ 6

w=open(file.txt, 'r')
print ('first line is : ',w.readline())
for line in w:  
    x= line
print ('last line is : ',x)
w.close()

Цикл for пробегает строки, а x получает последнюю строку на последней итерации.

Ответ 7

with open("myfile.txt") as f:
    lines = f.readlines()
    first_row = lines[0]
    print first_row
    last_row = lines[-1]
    print last_row

Ответ 8

Это мое решение, совместимое с Python3. Он также управляет пограничными случаями, но он пропускает поддержку utf-16:

def tail(filepath):
    """
    @author Marco Sulla ([email protected])
    @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()

Ответа на этот вопрос Trasp answer и комментарий AnotherParker.

Ответ 9

Получение первой строки тривиально легко. Для последней строки, предполагая, что вы знаете приблизительную верхнюю границу длины строки, os.lseek некоторую сумму от SEEK_END найдите вторую для последняя строка заканчивается, а затем readline() в последней строке.

Ответ 10

Вот расширение ответа @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