Python - 解析文本文件并根据某些条件创建列表

时间:2014-08-15 05:05:23

标签: python parsing

我一直在寻找这个问题的解决方案一段时间没有运气。我想使用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”或文件结尾等等)之间取每一行并将其放入列表中。对于此示例,它将导致两个列表。

我尝试了各种各样的东西,但他们都需要事先知道有关该文件的信息。我希望能够实现自动化。非常感谢你!

4 个答案:

答案 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]]}

那么,这是如何工作的:

  1. 我们使用mmapapply a regex to a file
  2. 我们假设标签是^\w+$的形式(即由一行上的字母和数字组成的标签)
  3. 然后捕获
  4. 之后的所有数字和空格
  5. 使用标签作为键创建一个dict,将解析后的数字作为后面的浮点列表。
  6. 完成!

答案 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)