2013-01-04 17 views
2

我有一個輸入文件,它是我想用python正則表達式解析的Fortran「名單」格式。展示最簡單的方法是用一個虛構的例子:在名單輸入文件中正則表達式解析鍵值對

$VEHICLES 
CARS= 1, 
TRUCKS = 0, 
PLAINS= 0, TRAINS = 0, 
LIB='AUTO.DAT', 
C This is a comment 
C Data variable spans multiple lines 
DATA=1.2,2.34,3.12, 
     4.56E-2,6.78, 
$END 
$PLOTTING 
PLOT=T, 
PLOT(2)=12, 
$END 

所以按鍵可以包含常規變量名稱字符以及括號和數字。這些值可以是字符串,布爾值(T,F,.T。,.F。,TRUE,FALSE,.TRUE。,.FALSE。都是可能的),整數,浮點數或逗號分隔的數字列表。鍵以相同的符號連接到它們的值。鍵值對由逗號分隔,但可以共享一行。對於長數字列表,值可以跨越多行。註釋是以C開頭的任何行。'='和','之前和之後的間距通常不一致。

我想出了一個工作正則表達式來解析鍵和值,並將它們放入有序字典(需要保持輸入順序)。

這是我的代碼到目前爲止。我已經包含了從閱讀文件到保存到字典的全部內容。

import re 
from collections import OrderedDict 

f=open('file.dat','r') 
file_str=f.read() 

#Compile regex pattern for requested namelist 
name='Vehicles' 

p_namelist = re.compile(r"\$"+name.upper()+"(.*?)\$END",flags=re.DOTALL|re.MULTILINE) 

#Execute regex on file string and get a list of captured tokens 
m_namelist = p_namelist.findall(file_str) 

#Check for a valid result 
if m_namelist: 
    #The text of the desired namelist is the first captured token 
    namelist=m_namelist[0] 

#Split into lines 
lines=namelist.splitlines() 

#List comprehension which returns the list of lines that do not start with "C" 
#Effectively remove comment lines 
lines = [item for item in lines if not item.startswith("C")] 

#Re-combine now that comment lines are removed 
namelist='\n'.join(lines) 

#Create key-value parsing regex 
p_item = re.compile(r"([^\s,\=]+?)\s*=\s*([^=]+)(?=[\s,][^\s,\=]+\s*\=|$)",flags=re.DOTALL|re.MULTILINE) 

#Execute regex 
items = p_item.findall(namelist) 

#Initialize namelist ordered dictionary 
n = OrderedDict() 

#Remove undesired characters from value  
for item in items: 
    n[item[0]] = item[1].strip(',\r\n ') 

我的問題是我是否正確解決這個問題。我意識到有一個ConfigParser庫,我還沒有嘗試過。我在這裏的重點是正則表達式:

([^\s,\=]+?)\s*=\s*([^=]+)(?=[\s,][^\s,\=]+\s*\=|$) 

,但我繼續包括爲徹底其它代碼,並證明什麼,我用它做。對於我的正則表達式,因爲這些值可以包含逗號,並且鍵值對也用逗號分隔,所以沒有簡單的方法來隔離這些對。我選擇了使用前瞻性查找下一個鍵和「=」。這允許「=」和下一個鍵之間的所有內容都是值。最後,因爲這對最後一對不起作用,所以我在「| $」中插入了前向預覽,這意味着如果找不到另一個「VALUE =」,則查找字符串的結尾。我認爲匹配值與[^ =] +然後是前瞻比嘗試匹配所有可能的值類型要好。

在寫這個問題,我想出了一個替代常規Expresson,它利用的事實,數字是可以在列表中唯一的價值優勢:

([^\s,\=]+?)\s*=\s*((?:\s*\d[\d\.\E\+\-]*\s*,){2,}|[^=,]+) 

這一個匹配任2列表或更多數字與(?:\s*\d[\d\.\E\+\-]*\s*,){2,}或任何與[^=,]下一個逗號之前的任何數字。

這些有點凌亂正則表達式是解析文件的最佳方式嗎?

+0

你爲什麼在這裏使用正則表達式堅持:

下面是示例輸出由腳本解析FORTRAN代碼示例後產生的?另外,爲什麼訂單很重要?我的印象是Fortran名單沒有訂購.. – mgilson

+0

我正在構建一個編輯輸入文件的應用程序。一些名單有數百個條目,通常分爲邏輯類別。此外,相關或相互依賴的輸入通常一起列出,以便它們可以很容易地一起編輯。我希望能夠讀入輸入文件,提供用於編輯各種參數的GUI,然後以相同的順序將其寫回。我實際上已經有了GUI和導出功能(使用硬編碼數據集),現在我正在編寫一個導入功能。 – flutefreak7

回答

2

我建議開發一點更復雜的解析器。

我偶然發現了谷歌代碼託管的項目,它實現了非常類似的解析器功能:Fortran Namelist parser for Python prog/scripts,但是它的構建方式很不一樣。 我打這一點,並更新其支持的格式的結構在你的例子:

請參閱要點我的版本: Updated Fortran Namelist parser for python https://gist.github.com/4506282

我希望這個解析器將幫助您與您的項目。

{'PLOTTING': 
    {'par': 
     [OrderedDict([('PLOT', ['T']), ('PLOT(2) =', ['12'])])], 
    'raw': ['PLOT=T', 'PLOT(2)=12']}, 
'VEHICLES': 
    {'par': 
     [OrderedDict([('TRUCKS', ['0']), ('PLAINS', ['0']), ('TRAINS', ['0']), ('LIB', ['AUTO.DAT']), ('DATA', ['1.2', '2.34', '3.12', '4.56E-2', '6.78'])])], 
    'raw': 
       ['TRUCKS = 0', 
        'PLAINS= 0, TRAINS = 0', 
        "LIB='AUTO.DAT'", 
        'DATA=1.2,2.34,3.12', 
        '4.56E-2,6.78']}} 
+0

非常感謝您在此回覆中的努力!我最終找到了你提到的Fortran Namelist解析器,並偷走了一些正則表達式,以幫助確定每個鍵值對的值的數據類型。最後,我堅持使用相當簡單的正則表達式解析方法,因爲我理解它。儘管如此,您的答案無疑是一個針對我的問題的強大的量身定製解決方案!爲您的辛勤工作而讚歎!也許我會最終使用它來改進我的代碼,只要我可以將我的頭圍繞在它所做的一切事情上! – flutefreak7

+0

謝謝,這非常有用!在我看來,這是從以前的代碼版本開始的一步。 –