python3 использует генератор для фильтрации файлов с многострочными записями - PullRequest
0 голосов
/ 29 мая 2019

Мне нужно прочитать огромные файлы, структурированные как многострочные записи, и записать в файл записи с определенными индексами, скажем, номерами записей R = 1, 2 и 1093. Если записи имеют N = 3 строки каждая, это равносильно чтению строки файла:затем введите строки с номерами 1, 2, 3 и 4, 5, 6 и 3277, 3278, 3279 (первая строка в каждой записи Ri начинается с номера строки Ri-1 * N + 1.

Полагаю, можно вычислить строки для записи, проходить по файлу построчно и записывать эти строки, однако можно ли «сжать» последовательные строки 1, 2 и 3 в объект-генератор, содержащий записи, и каким-то образом их отфильтровать илибудет ли печатать их напрямую в файл, если они перечисляются в R? Что-то в этом псевдокоде:

def subset(file_in, file_out, N, R):
    with open(file_in, "rt") as fin, open(file_out, "wt") as fout:
        line = (line.rstrip() for line in fin)
        record = enumerate(zip(line, line, line)) # What if records are of size N
        for i, r in record if i in R:
            fout.write(r)

Что делать, если вы хотите, чтобы размер записи N был параметром?

ОБНОВЛЕНИЕПРИМЕР

Пример для file_in (4 записи, 3 строки / запись):

dslfkj
2
a
dflkj
3
g
fds
2
b
fsdlkj
1
n

Тогда подмножество (file_in, file_out, 3, [1,3]) даст(file_out)

dslfkj
2
a
fds
2
b

1 Ответ

1 голос
/ 29 мая 2019

Для этой проблемы имеет смысл просто решить эту проблему построчно, используя разделение по полу.

Например:

fin = '''
dslfkj
2
a
dflkj
3
g
fds
2
b
fsdlkj
1
'''

line_gen = (line.rstrip() for line in fin.strip().split())

R = [1, 3]
R = [val - 1 for val in R] #zero indexing
N = 3
for i, line in enumerate(line_gen):
    if i // N in R:
        print(line)

Выход:

dslfkj
2
a
fds
2
b

Ваша функция может выглядеть примерно так: (вы можете проверить, работает ли она «из коробки» или требует настройки. Я не проверял часть открытия файла.

def subset(file_in, file_out, N, R):
    R = [val - 1 for val in R] #zero indexing
    with open(file_in, "rt") as fin, open(file_out, "wt") as fout:
        line_gen = (line.rstrip() for line in fin)
        for i, line in enumerate(line_gen):
            if i // N in R:
                fout.write(line)
                fout.write('\n')

Изменить: Ответ ниже относится к тому, как вы могли бы использовать генераторы и сгруппировать значения вместе. Сказав это, я не думаю, что вам нужно использовать его. Однако, если вы все еще хотите, вы можете построить свою функцию на основе этого.

Старый ответ:

Вы можете создать n ссылки на объект с помощью списка, а затем распаковать его, используя оператор * (он же splat) .

Например:

from itertools import zip_longest
line = (x for x in range(100, 132))
n = 3
record = zip(*([line] * n)) #equivalent to *[line, line, line] which is unpacked into zip arguments
for i, r in enumerate(record):
    print(i, r)

0 (100, 101, 102)
1 (103, 104, 105)
2 (106, 107, 108)
3 (109, 110, 111)
4 (112, 113, 114)
5 (115, 116, 117)
6 (118, 119, 120)
7 (121, 122, 123)
8 (124, 125, 126)
9 (127, 128, 129)

Кроме того, в зависимости от того, что вы хотите сделать с «оставшимися» строками, вы можете использовать zip_longest вместо этого.

...