2016-04-15 2 views
4

Я действительно больше не верю в общий синтаксический анализатор текстовых файлов - особенно эти файлы предназначены для читателей. Такие файлы, как HTML и веб-журнал, могут обрабатываться красивым мылом или регулярным выражением. Но человеческий читаемый текстовый файл по-прежнему является жесткой гайкой для взлома.Дизайн модуля для разбора текстового файла

Только, что я готов вручную закодировать синтаксический анализатор текстовых файлов, адаптируя каждый другой формат, с которым я столкнулся. Я все еще хочу посмотреть, возможно ли иметь лучшую структуру программы таким образом, что я все еще смогу понять логику программы через 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» As «in s:» здесь? – usr2564301

ответ

3

Рассмотрим отображение использования словаря:

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

Тогда вам нужно только использовать его как это:

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 вместе с ключевым словом:

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

Спасибо Яну! Я пробовал этот подход. Просто мне нужно обновить dmap каждый раз, когда я добавляю новую функцию.Все еще ищут даже более ленивый подход: 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!') 

или с Dict как Ян отметил:

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 будет список либо с одним или двумя элементами для примеров, приведенных.

+0

Спасибо Стив! Регулярное выражение решение очень аккуратно и приятно! – chapter3