Как разбить файл на куски по строковому разделителю в Python

Мне нужно загрузить потенциально большой файл CSV в мое приложение. Каждый раздел этого файла обозначен #TYPE *, Как мне разбить его на куски и выполнить дальнейшую обработку для каждого чанка? Каждый блок представляет собой список заголовков, за которыми следуют все значения.

Прямо сейчас я написал обработку для одного чанка, но я не уверен, как выполнить операцию для каждого чанка. Я думаю, что операция регулярного выражения будет лучшим вариантом из-за постоянного возврата #TYPE *,

#TYPE Lorem.Text.A
...
#TYPE Lorem.Text.B
...
#TYPE Lorem.Text.C
...

ОБНОВИТЬ

Это решение было изменено с сохранения всех разделов в одном файле до сохранения всех разделов в отдельных файлах и архивирования их в ZIP-файл. Этот zip-файл читается python и дополнительно анализируется. Если кому-то будет интересно это объяснение, напишите мне, и я обновлю этот вопрос.

Ответ от @Padraic был самым полезным для старого курса.

2 ответа

Решение

Вы можете использовать группу, предполагая, что разделы разделены линиями, начинающимися с #TYPE:

from itertools import groupby, chain


def get_sections(fle):
    with open(fle) as f:
        grps = groupby(f, key=lambda x: x.lstrip().startswith("#TYPE"))
        for k, v in grps:
            if k:
                yield chain([next(v)], (next(grps)[1]))  # all lines up to next #TYPE

Вы можете получить каждый раздел в процессе итерации:

In [13]: cat in.txt
#TYPE Lorem.Text.A
first
#TYPE Lorem.Text.B
second
#TYPE Lorem.Text.C
third

In [14]: for sec in get_sections("in.txt"):
   ....:     print(list(sec))
   ....:     
['#TYPE Lorem.Text.A\n', 'first\n']
['#TYPE Lorem.Text.B\n', 'second\n']
['#TYPE Lorem.Text.C\n', 'third\n']

Если никакие другие строки не начинаются с # тогда одного этого будет достаточно, чтобы использовать его при запуске, в вашем паттерне нет ничего сложного, так что на самом деле это не тот случай использования регулярного выражения. Это также сохраняет только раздел за раз, а не весь файл в памяти.

Если у вас нет ведущих пробелов и единственное место # Появится перед ТИПОМ, может быть достаточно просто вызвать groupby:

from itertools import groupby, chain


def get_sections(fle):
    with open(fle) as f:
        grps = groupby(f)
        for k, v in grps:
            if k:
                yield chain([next(v)], (next(grps)[1]))  # all lines up to next #TYPE

Если в начале были какие-то метаданные, вы могли бы использовать пропущенные строки, чтобы пропустить строки #Type а затем просто группа:

from itertools import groupby, chain, dropwhile


def get_sections(fle):
    with open(fle) as f:
        grps = groupby(dropwhile(lambda x: not x.startswith("#"), f))
        for k, v in grps:
            if k:
                yield chain([next(v)], (next(grps)[1]))  # all lines up to next #TYPE

Демо-версия:

In [16]: cat in.txt
meta
more meta
#TYPE Lorem.Text.A
first
#TYPE Lorem.Text.B
second
second
#TYPE Lorem.Text.C
third

In [17]: for sec in get_sections("in.txt"):
            print(list(sec))
   ....:     
['#TYPE Lorem.Text.A\n', 'first\n']
['#TYPE Lorem.Text.B\n', 'second\n', 'second\n']
['#TYPE Lorem.Text.C\n', 'third\n']

Делаем ли расщепление по новой строке #TYPE

chunks = re.split(r'\n(?=#TYPE\b *)', f.read())

Пример:

>>> import re
>>> s = '''#TYPE Lorem.Text.A
...
#TYPE Lorem.Text.B
...
#TYPE Lorem.Text.C
...'''
>>> re.split(r'\n(?=#TYPE *)', s)
['#TYPE Lorem.Text.A\n...', '#TYPE Lorem.Text.B\n...', '#TYPE Lorem.Text.C\n...']
>>> 
Другие вопросы по тегам