解决方案
是的,这是解析的痛苦。但是,您实际上不需要很多正则表达式。普通的 split
可能足以将本文档分解为可管理的字符串序列。
这些是我所说的“头部”文本块。您有标题,一行“ - ”,然后是数据。
您要做的是将“头体”结构崩溃到产生单个词典的发电机函数中。
def get_means_intecepts_thresholds( source_iter ):
"""Precondition: Current line is a "MEANS/INTERCEPTS/THRESHOLDS" line"""
head= source_iter.next().strip().split()
junk= source_iter.next().strip()
assert set( junk ) == set( [' ','-'] )
for line in source_iter:
if len(line.strip()) == 0: continue
if line.strip() == "SLOPES": break
raw_data= line.strip().split()
data = dict( zip( head, map( float, raw_data[1:] ) ) )
yield int(raw_data[0]), data
def get_slopes( source_iter ):
"""Precondition: Current line is a "SLOPES" line"""
head= source_iter.next().strip().split()
junk= source_iter.next().strip()
assert set( junk ) == set( [' ','-'] )
for line in source_iter:
if len(line.strip()) == 0: continue
if line.strip() == "SLOPES": break
raw_data= line.strip().split() )
data = dict( zip( head, map( float, raw_data[1:] ) ) )
yield raw_data[0], data
关键是要用一组操作消耗头和垃圾。
然后消耗使用另一组操作的数据行。
由于这些是发电机,因此您可以将它们与其他操作结合在一起。
def get_estimated_sample_statistics( source_iter ):
"""Precondition: at the ESTIMATED SAMPLE STATISTICS line"""
for line in source_iter:
if len(line.strip()) == 0: continue
assert line.strip() == "MEANS/INTERCEPTS/THRESHOLDS"
for data in get_means_intercepts_thresholds( source_iter ):
yield data
while True:
if len(line.strip()) == 0: continue
if line.strip() != "SLOPES": break
for data in get_slopes( source_iter ):
yield data
这样的东西可能比正则表达式更好。
其他提示
根据您的示例,您拥有的是一堆不同的嵌套子形式,它们单独地很容易解析。可能是压倒性的是格式的数量以及它们可以以不同的方式嵌套的事实。
在最低级别上,您在一条线上具有一组白空间分隔的值。这些线结合成块,块如何将块相互结合和嵌套是复杂的部分。这种类型的输出是为人类阅读而设计的,从未打算被“刮擦”回机器可读形式。
首先,我将联系该软件的作者,并找出是否有可用的替代输出格式,例如XML或CSV。如果正确完成(即,不仅仅是包裹在笨拙的XML或更换空格的逗号的印刷形式),这将更容易处理。如果我不愿意想出一个格式的层次列表及其筑巢方式。例如,
ESTIMATED SAMPLE STATISTICS
开始一个块- 在该块内
MEANS/INTERCEPTS/THRESHOLDS
开始一个嵌套的块 - 接下来的两行是一组列标题
- 接下来是一个(或更多?)行数据,带有一个行标头和数据值
等等。如果您分别解决这些问题,您会发现它很乏味,但并不复杂。将上述每个步骤都视为测试输入以查看是否匹配的模块,然后致电其他模块以进一步测试可能发生的“内部”块的事物,如果您可以找到没有的东西,则会回溯t匹配您的期望(顺便说一句,这称为“递归下降”)。
请注意,无论如何您都必须执行这样的操作,以构建可以在其上操作的数据(“数据模型”)的内存版本。
我的建议是对线条进行粗略按摩以更有用的形式。这是您数据的一些实验:
from __future__ import print_function
from itertools import groupby
import string
counter = 0
statslist = [ statsblocks.split('\n')
for statsblocks in open('mlab.txt').read().split('\n\n')
]
print(len(statslist), 'blocks')
def blockcounter(line):
global counter
if not line[0]:
counter += 1
return counter
blocklist = [ [block, list(stats)] for block, stats in groupby(statslist, blockcounter)]
for blockno,block in enumerate(blocklist):
print(120 * '=')
for itemno,line in enumerate(block[1:][0]):
if len(line)<4 and any(line[-1].endswith(c) for c in string.letters) :
print('\n** DATA %i, HEADER (%r)**' % (blockno,line[-1]))
else:
print('\n** DATA %i, item %i, length %i **' % (blockno, itemno, len(line)))
for ind,subdata in enumerate(line):
if '___' in subdata:
print(' *** Numeric data starts: ***')
else:
if 6 < len(subdata)<16:
print( '** TYPE: %s **' % subdata)
print('%3i : %s' %( ind, subdata))
你可以尝试 pyparsing. 。它使您能够为要解析的内容编写语法。除了解析编程语言以外,它还有其他示例。但是我同意吉姆·加里森(Jim Garrison)的观点,即您的案子似乎并没有要求真正的解析器,因为写语法会很麻烦。我会尝试使用蛮力的解决方案,例如在空格处的分裂线。它不是万无一失的,但是我们可以假设输出是正确的,因此,如果一条线有 n 标题,下一行将完全有 n 值。
事实证明,这样的表格程序输出是我最早的pyparsing应用程序之一。不幸的是,这个确切的示例处理了我无法发布的专有格式,但是这里有一个类似的示例: http://pyparsing.wikispaces.com/file/view/dictexample2.py .