Как разбить файл на куски по строковому разделителю в 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...']
>>>