【问题标题】:Design a module to parse text file设计一个模块来解析文本文件
【发布时间】:2016-04-15 14:46:07
【问题描述】:

我真的不再相信通用文本文件解析器了——尤其是那些文件是为人类读者设计的。 Beautiful Soap 或正则表达式可以很好地处理 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

这不是我想要维护的东西。

有什么建议吗?曾经我认为装饰师可能会有所帮助,但我自己无法解决。感谢有人能指出我正确的方向。

【问题讨论】:

  • 选择一组短语意味着查看字典可以为您做什么。您可能还想让您的支票大小写无关紧要。我自己对 Python 还很陌生,你不应该在这里写if "As" in s: 吗?

标签: python text-parsing


【解决方案1】:

考虑使用字典映射:

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
}

【讨论】:

  • 谢谢伊恩!我已经尝试过这种方法。只是我每次添加新功能时都需要更新 dmap。仍在寻找更懒惰的方法:P
  • @chapter3 以上是不是懒惰?我简直不敢相信! :p
【解决方案2】:

怎么样

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!')

或使用 Ian 提到的字典:

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!')

【讨论】:

  • 感谢 Orelus!我喜欢你的 NotImplementedError!
【解决方案3】:

为什么不使用正则表达式?

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 将是一个列表,其中包含所提供示例的一个或两个元素。

【讨论】:

  • 谢谢史蒂夫!正则表达式的解决方案非常简洁漂亮!
猜你喜欢
  • 2010-09-24
  • 2012-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多