我一直在寻找这个问题的解决方案一段时间没有运气。我想使用Python来读取文本文件并根据文件中的数据创建一些列表(或数组)。一个例子将最能说明我的目标。
考虑以下文字:
NODE
1.0, 2.0
2.0, 2.0
3.0, 2.0
4.0, 2.0
ELEMENT
1, 2, 3, 4
5, 6, 7, 8
1, 2, 3, 4
1, 2, 3, 4
1, 2, 3, 4
5, 6, 7, 8
5, 6, 7, 8
5, 6, 7, 8
我想通读文件(理想情况下,文件可以很大一次),一旦我找到“NODE”,取“NODE”和“ELEMENT”之间的每一行并放入一个列表。然后,一旦我到达“ELEMENT”,在“ELEMENT”和其他一些中断(也许是另一个“ELEMENT”或文件结尾等等)之间取每一行并将其放入列表中。对于此示例,它将导致两个列表。
我尝试了各种各样的东西,但他们都需要事先知道有关该文件的信息。我希望能够实现自动化。非常感谢你!
答案 0 :(得分:4)
使用该示例数据,并假设标签遵循示例中的模式,您可以使用正则表达式:
import re, mmap, os
def conv(s):
try:
return float(s)
except ValueError:
return s
data_dict={}
with open(fn, 'r') as fin:
size = os.stat(fn).st_size
data = mmap.mmap(fin.fileno(), size, access=mmap.ACCESS_READ)
for m in re.finditer(r'^(\w+)$([\d\s,.]+)', data, re.M):
data_dict[m.group(1)]=[[conv(e) for e in line.split(',')]
for line in m.group(2).splitlines() if line.strip()]
print data_dict
打印:
{'NODE': [[1.0, 2.0], [2.0, 2.0], [3.0, 2.0], [4.0, 2.0]],
'ELEMENT': [[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0], [1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0], [5.0, 6.0, 7.0, 8.0], [5.0, 6.0, 7.0, 8.0]]}
那么,这是如何工作的:
^\w+$
的形式(即由一行上的字母和数字组成的标签)完成!
答案 1 :(得分:2)
如果您希望完全通用且自动化,则需要提出区分节标题与行的规则。我会发明一个,但它可能不是你想要的那个,在这种情况下,我发明的代码对你不起作用......但希望它能告诉你你需要做什么,以及如何开始。
def new_section(row):
return len(row) == 1 and row[0].isalpha() and row[0].isupper()
现在,我们可以使用itertools.groupby
按行分组行来对行进行分组。如果你打印出每个组,你会得到这样的结果:
True, [['NODE']]
False, [['1.0', '2.0'], ['2.0', '2.0'], …, ]
True, [['ELEMENT']]
False, [['1.0', '2.0', '3.0', '4.0'], …, ]
我们不关心每个中的第一个值,所以放弃它。
我们想要将每对相邻的组批处理成一个(标题,行)对,我们可以通过自己的迭代器来完成。
然后把它放在一个dict中,看起来像这样:
{'NODE': [['1.0', '2.0'], ['2.0', '2.0'], …],
'ELEMENT': [['1.0', '2.0', '3.0', '4.0'], …]}
以下是整个事情:
import csv
import itertools
def new_section(row):
return len(row) == 1 and row[0].isalpha() and row[0].isupper()
with open(path) as f:
rows = csv.reader(f)
grouped = itertools.groupby(rows, new_section)
groups = (group for key, group in grouped)
pairs = zip(groups, groups)
lists = {header[0][0]: rows for header, rows in pairs}
答案 2 :(得分:0)
def getBlocks(fname):
state = 0
node = []
ele = []
with open(fname) as f:
for line in f:
if "NODE" in line:
if state == 2:
yield (node,ele)
node,ele = [],[]
state = 1
elif state == 1 and "ELEMENT" in line:
state = 2
elif state == 1:
node.append(list(map(float,line.split(","))))
elif state == 2 and re.match("[a-zA-Z]+",line):
yield (node,ele)
node,ele = [],[]
state = 0
elif state == 2:
ele.append(list(map(int,line.split(","))))
yield (node,ele)
for node,ele in getBlocks("somefile.txt"):
print "N:",node
print "E:",ele
可能是关于你正在寻找它有点粗...我相信你可以做得更好
答案 3 :(得分:0)
对于更新问题中的更简单问题,您实际上不需要regexp或groupby,或复杂的状态机,或者新手应该能够轻松理解的任何内容。
您需要做的就是将行累积到一个列表中,直到找到行'ELEMENT'
,然后开始将行累积到另一行中。像这样:
import csv
result = {'NODES': [], 'ELEMENTS': []}
current = result['NODES']
with open(path) as f:
for row in csv.reader(f):
if row == ['NODE']:
pass
elif row == ['ELEMENT']:
current = result['ELEMENTS']
else:
current.append(row)