2

Python の正規表現で解析したい Fortran の「namelist」形式の入力ファイルがあります。実証する最も簡単な方法は、架空の例を使用することです。

$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=" が見つからない場合は文字列の末尾を探すことを意味します。[^=]+ とそれに続く先読みを使用して値を一致させる方が、考えられるすべての値の型を一致させようとするよりも優れていると考えました。

この質問を書いているときに、リストに入れることができる値は数値だけであるという事実を利用する代替の正規表現を思いつきました。

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

これは、2 つ以上の数字のリストまたは(?:\s*\d[\d\.\E\+\-]*\s*,){2,}次のカンマの前にあるものと一致します[^=,]

これらのやや乱雑な正規表現は、このようなファイルを解析するための最良の方法ですか?

4

1 に答える 1

3

もう少し洗練されたパーサーを開発することをお勧めします。

非常によく似たパーサー機能を実装する Google コード ホスティングのプロジェクトに出くわしました: Fortran Namelist parser for Python prog/scriptsですが、少し異なる形式用にビルドされています。少し遊んで、あなたの例のフォーマットの構造をサポートするように更新しました:

gistで私のバージョンを参照してください: Updated Fortran Namelist parser for python https://gist.github.com/4506282

このパーサーがあなたのプロジェクトに役立つことを願っています。

FORTRAN コード例を解析した後にスクリプトによって生成される出力例を次に示します。

{'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']}}
于 2013-01-10T22:41:21.900 に答える