2016-04-15 12 views
4

私は本当に一般的なテキストファイルパーサーを信じていません。特に、それらのファイルは人間の読者のためのものです。 HTMLやWebログなどのファイルは、美しい石鹸や正規表現でうまく処理できます。しかし、人間が判読可能なテキストファイルは、依然としてひどく厳しいものです。テキストファイルを解析するモジュールを設計する

テキストファイルパーサーを手作業でコーディングして、私が遭遇するすべての異なるフォーマットを調整しています。私はまだ3ヶ月後のプログラムロジックを理解できる方法で、より良いプログラム構造を持つことが可能かどうかを見たいと思っています。また、それを読みやすくする。

今日、私は、ファイルからタイムスタンプを抽出するために、問題を与えられました。

"As of 12:30:45, ..." 
"Between 1:12:00 and 3:10:45, ..." 
"During this time from 3:44:50 to 4:20:55 we have ..." 

解析が簡単です。私は、各行の異なる場所にタイムスタンプを持っています。しかし、私はどのようにモジュール/関数を(1)各ラインフォーマットが別々に扱われるか、(2)関連する関数に分岐する方法で設計すべきかと思います。例えば、私はこのように、各ラインパーサーをコーディングすることができます。

def parse_as(s): 
    return s.split(' ')[2], s.split(' ')[2] # returning the second same as the first for the case that only one time stamp is found 

def parse_between(s): 
    return s.split(' ')[2], s.split(' ')[4] 

def parse_during(s): 
    return s.split(' ')[4], s.split(' ')[6] 

これは、すでにプログラムで扱うフォーマットについて迅速なアイデアを持っているために私を助けることができます。別の新しいフォーマットに遭遇した場合に備えて、私はいつも新しい機能を追加することができます。

しかし、私はまだ関連する機能に枝分かれするエレガントな方法がありません。

# open file 
for l in f.readline(): 
    s = l.split(' ') 
    if s == 'As': 
     ts1, ts2 = parse_as(l) 
    else: 
     if s == 'Between': 
      ts1, ts2 = parse_between(l) 
     else: 
      if s == 'During': 
      ts1, ts2 = parse_during(l) 
      else: 
      print 'error!' 
    # process ts1 and ts2 

これは私が維持したいことではありません。

提案がありますか?かつて私はデコレータが助けるかもしれないと思ったが、私はそれを自分で分類することができなかった。誰かが私に正しい方向を向けることができればと感謝します。

+0

特定のフレーズセットを選択すると、辞書があなたのためにできることを調べることができます。また、小切手を無関係にすることもできます。 Pythonには全く新しいので、 '' if ''を '' s ''と書いてはいけませんか? – usr2564301

答えて

3

辞書マッピングを使用しての考えてみましょう:

dmap = { 
    'As': parse_as, 
    'Between': parse_between, 
    'During': parse_during 
} 

for l in f.readline(): 
    s = l.split(' ') 
    p = dmap.get(s, None) 
    if p is None: 
     print('error') 
    else: 
     ts1, ts2 = p(l) 
     #continue to process 

維持するために非常に簡単:

dmap = { 
    'As': parse_as, 
    'Between': parse_between, 
    'During': parse_during 
} 

は、その後あなただけの、このようにそれを使用する必要があります。新しい機能を持っている場合は、あなただけのキーワードと一緒dmapにそれを追加する必要があります。

dmap = { 
    'As': parse_as, 
    'Between': parse_between, 
    'During': parse_during, 
    'After': parse_after, 
    'Before': parse_before 
    #and so on 
} 
+0

ありがとうIan!私はこのアプローチを試みました。ちょうど新しい機能を追加するたびにdmapを更新する必要があります。それでもなおlazierアプローチを探しています:P – chapter3

+1

@ chapter3上記は怠惰ではありません*十分*?私はそれを信じることができない! :p – Ian

1

何述べたイアン

start_with = ["As", "Between", "During"] 
parsers = [parse_as, parse_between, parse_during] 


for l in f.readlines(): 
    match_found = False 

    for start, f in zip(start_with, parsers): 
     if l.startswith(start): 
      ts1, ts2 = f(l.split(' ')) 
      match_found = True 
      break 

    if not match_found: 
     raise NotImplementedError('Not found!') 

についてや として辞書に:

rules = { 
    "As": parse_as, 
    "Between": parse_between, 
    "During": parse_during 
} 

for l in f.readlines(): 
    match_found = False 

    for start, f in rules.items(): 
     if l.startswith(start): 
      ts1, ts2 = f(l.split(' ')) 
      match_found = True 
      break 

    if not match_found: 
     raise NotImplementedError('Not found!') 
+0

ありがとうOrelus!私はあなたのNotImplementedErrorが好きです! – chapter3

0

なぜ正規表現を使用しないのですか?

import re 

# open file 
with open('datafile.txt') as f: 
    for line in f: 
     ts_vals = re.findall(r'(\d+:\d\d:\d\d)', line) 
     # process ts1 and ts2 

したがって、ts_valsは、提供される例の1つまたは2つの要素のリストです。

+0

ありがとうSteve!正規表現のソリューションは非常にきれいでいいです! – chapter3