#python
#python
Вопрос:
У меня есть файл размером около 100 МБ, который выглядит следующим образом:
#meta data 1
skadjflaskdjfasljdfalskdjfl
sdkfjhasdlkgjhsdlkjghlaskdj
asdhfk
#meta data 2
jflaksdjflaksjdflkjasdlfjas
ldaksjflkdsajlkdfj
#meta data 3
alsdkjflasdjkfglalaskdjf
Этот файл содержит одну строку метаданных, которая соответствует нескольким данным переменной длины, содержащим только буквенно-цифровые символы. Каков наилучший способ считывания этих данных в простой список, подобный этому:
data = [[#meta data 1, skadjflaskdjfasljdfalskdjflsdkfjhasdlkgjhsdlkjghlaskdjasdhfk],
[#meta data 2, jflaksdjflaksjdflkjasdlfjasldaksjflkdsajlkdfj],
[#meta data 3, alsdkjflasdjkfglalaskdjf]]
Моей первоначальной идеей было использовать read()
метод для чтения всего файла в память, а затем использовать регулярные выражения для преобразования данных в желаемый формат. Есть ли лучший, более питонический способ? Все строки метаданных начинаются с восьмистишия, а все строки данных являются буквенно-цифровыми. Спасибо!
Ответ №1:
itertools.groupby предоставляет простой способ объединения строк в группы:
import itertools
data=[]
with open('data.txt','r') as f:
for key,group in itertools.groupby(f,lambda line: line.startswith('#meta')):
if key:
meta=next(group).strip()
else:
lines=''.join(group).strip()
data.append((meta,lines))
print(data)
выдает
[('#meta data 1', 'skadjflaskdjfasljdfalskdjflnsdkfjhasdlkgjhsdlkjghlaskdjnasdhfk'), ('#meta data 2', 'jflaksdjflaksjdflkjasdlfjasnldaksjflkdsajlkdfj'), ('#meta data 3', 'alsdkjflasdjkfglalaskdjf')]
Выражение
itertools.groupby(f,lambda line: line.startswith('#meta'))
возвращает итератор. Он перебирает строки в f
и вызывает lambda
функцию в каждой строке. Когда он встречает строку, начинающуюся с #meta
, эта функция возвращает True
, в противном случае False
.
itertools.groupby
собирает все смежные строки, которые возвращают одно и то же значение.
Итак, строка, начинающаяся с #meta
, помещается в свою собственную группу, затем все последующие строки, не начинающиеся с #meta
, помещаются в следующую группу и так далее.
key
Это возвращаемое значение из lambda
функции. В этом случае это будет либо True
, либо False
.
Комментарии:
1. Вау, это здорово! Единственное, с чем у меня возникают трудности, это то, что мой вывод выдает мне
[(False, 'skadjflaskdjfasljdfalskdjflnsdkfjhasdlkgjhsdlkjghlaskdjnasdhfk')...
Я, кажется, не понимаю, почему я получаю логическое значение и почему оно равно false?!2. Похоже, что, возможно, вы печатаете
key
, а неmeta
? Используете ли выdata.append((key,lines))
? Если да, изменитеkey
—>meta
.
Ответ №2:
Я не знаю, будет ли это самым быстрым способом, но из головы:
data = []
with open('input.file', 'r') as fp:
for line in fp:
line = line.strip()
if line[0] == '#':
data.append((line, []))
else:
data[-1][1].append(line)
data = [(X, ''.join(Y)) for X, Y in data]
Комментарии:
1. Спасибо, это был классный ответ. Я никогда не думал делать это таким образом.
Ответ №3:
Я думаю, что-то вроде этого:
result = []
for line in file.readlines():
if line[0] == '#':
result.append([line])
else:
if len(result[-1]) == 1:
result[-1].append(line)
else:
result[-1][-1] = line
Не тестировался.
Ответ №4:
Я бы сделал это просто, что-то вроде:
data = [] # result
lastmeta = None # the last metadata line seen
chunks = [] # lines since the last metadata line
for line in input:
if line[0] == '#': # metadata
if lastmeta: # need to flush data we've collected
data.append((lastmeta, ''.join(chunks))
lastmeta = line
else:
chunks.append(line)