Я хочу прочитать две непоследовательные строки по одному разу в очень большом файле в python, то есть ожидаемый результат следующий (номер - номер строки):

1 6 (первый раз читает 1-ю и 6-ю строку)

2 7 (второй раз читает 2-ю и 7-ю строку)

3 8 (третий раз читает 3-ю и 8-ю строку)

4 9

5 10

6 11

7 12

8 13

9 14

10 15

..

..

..

..

.. (последний раз читает 6-е число из последней и последней строки)

Номер строки можно получить с помощью метода enumerate (объект файла). Я новичок в Python, и просто знаю, как читать две строки подряд один раз. Не могли бы вы поделиться со мной, как получить ожидаемый результат выше? Спасибо за ваше время!

1
jing 13 Мар 2018 в 04:16

2 ответа

Лучший ответ

Если вы хотите одну итерацию по файлу (прочитайте файл один раз), вы можете написать вариацию рецепта itertools pairwise, продвигая второй итератор на 5 вместо 1 (используя {{X2} } ) . По сути, это то же самое, что и ответ @ Blender, в котором используется deque, но функциональный способ решения проблемы, о котором может быть проще рассуждать (и доказать, что реализация верна без крайних случаев).

Из книги рецептов itertools python3 :

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

def consume(iterator, n):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

Пример решения:

def pairwise(iterable, n=1):
    """Return every pair of items, separated by n items, from iterable
    s -> (s[0], s[n]), (s[1], s[n+1]), (s[2], s[2+n]), ..."""
    assert n >= 1
    a, b = tee(iterable)
    # advance leading iterator (b) by n elements
    consume(b, n)
    return zip(a, b)

with open(filename, 'r') as f:
    for a, b in pairwise(f, 5):
        print('{}: {}'.format(a, b))

И решение collections.deque, и решение pairwise требуют одновременного хранения в памяти n + 1 элементов плюс накладные расходы для контейнеров. Открытие файла дважды с помощью двух файловых указателей уменьшает объем памяти до 2 элементов, но может быть значительно медленнее из-за удвоенного числа обращений к дисковым операциям ввода-вывода (при условии, что ОС не кэширует чтения из файла). Чтение из файла дважды немного опаснее, поскольку файл может быть изменен во время чтения, что приводит к противоречивым данным. Чтобы быть справедливым, изменение файла при повторении его один или два раза приведет к сомнительным результатам.

Само собой разумеется, что если вы (все еще) используете Python 2.7, замените zip на itertools.izip, если только вы не хотите сохранить все содержимое файла в ОЗУ и, возможно, вывести из строя вашу программу или систему для большого ввода файлы .

1
IceArdor 14 Мар 2018 в 19:59

Вы можете открыть файл несколько раз. Просто пропустите первые пару строк во втором дескрипторе файла и используйте zip, чтобы прочитать их обе одновременно:

with open(filename, 'r') as handle1, \
     open(filename, 'r') as handle2:
    for _ in range(5):
        handle2.readline()

    for line1, line2 in zip(handle1, handle2):
        print(line1, line2)

Вам придется from itertools import izip as zip в Python 2, чтобы zip не читал весь файл дважды в память.

Альтернативным решением будет использование двусторонней очереди и сохранение 5 + 1 строк в памяти при чтении из файла:

from collections import deque

lines = deque([], maxlen=6)

with open(filename, 'r') as handle:
    for line in handle:
        lines.append(line)

        if len(lines) == lines.maxlen:
            print(lines[0], lines[-1])

Это читает файл только один раз, но если ваш файл действительно длинный и две строки разделены более чем на 5, вам придется хранить столько строк в памяти.

3
Blender 13 Мар 2018 в 01:37