[英]Parsing blockbased program output using Python
根據您的示例,您擁有一堆不同的嵌套子格式,可以很容易地對其進行單獨解析。 令人不知所措的是,格式的數量巨大,而且它們可以以不同的方式嵌套。
在最低級別上,您在一行上有一組用空格分隔的值。 這些線組合成塊,而這些塊如何相互組合和嵌套是復雜的部分。 這種類型的輸出是專為人類閱讀而設計的,決不會被“刮”回機器可讀的形式。
首先,我將與軟件的作者聯系,並確定是否存在其他可用的輸出格式,例如XML或CSV。 如果正確完成(即不僅是包裝在笨拙的XML中的打印格式,或者用逗號替換空白),這將更容易處理。 如果不這樣做,我將嘗試提出格式的分層列表以及它們如何嵌套。 例如,
ESTIMATED SAMPLE STATISTICS
開始一個塊 MEANS/INTERCEPTS/THRESHOLDS
開始一個嵌套塊 等等。 如果分別解決每個問題,您會發現它很繁瑣但並不復雜。 將上述每個步驟都視為測試輸入以查看其是否匹配的模塊,然后調用其他模塊以進一步測試可能在“內部”發生的事情,如果發現不匹配的內容則回溯。 t符合您的期望(順便說一下,這稱為“遞歸”)。
請注意,無論如何,您都必須執行類似的操作,以便構建可以在其上操作的數據的內存版本(“數據模型”)。
是的,這很難解析。 但是,您實際上並不需要很多正則表達式。 普通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
這樣的事情可能比正則表達式更好。
我的建議是將線條粗略地按摩成更有用的形式。 這是對您的數據進行的一些實驗:
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 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.