设计一个解析文本文件的模块
Design a module to parse text file
我真的不再相信通用文本文件解析器了——尤其是那些供人类阅读的文件。 HTML 和 web log 这样的文件可以用 Beautiful Soap 或 Regular Expression 很好地处理。但是人类可读的文本文件仍然是一个棘手的问题。
只是我愿意手工编写一个文本文件解析器,定制我会遇到的每一种不同格式。我仍然想看看是否有可能以我在 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 ..."
解析很简单。我在每条线上的不同位置都有时间戳。但我认为我应该如何设计 module/function 的方式:(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
那不是我想要维护的东西。
有什么建议吗?曾经我认为装饰器可能会有所帮助,但我自己无法解决。如果有人能指出正确的方向,我将不胜感激。
考虑使用字典映射:
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
}
那
呢
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!')
为什么不使用正则表达式?
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
将是一个包含一个或两个元素的列表,用于提供的示例。
我真的不再相信通用文本文件解析器了——尤其是那些供人类阅读的文件。 HTML 和 web log 这样的文件可以用 Beautiful Soap 或 Regular Expression 很好地处理。但是人类可读的文本文件仍然是一个棘手的问题。
只是我愿意手工编写一个文本文件解析器,定制我会遇到的每一种不同格式。我仍然想看看是否有可能以我在 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 ..."
解析很简单。我在每条线上的不同位置都有时间戳。但我认为我应该如何设计 module/function 的方式:(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
那不是我想要维护的东西。
有什么建议吗?曾经我认为装饰器可能会有所帮助,但我自己无法解决。如果有人能指出正确的方向,我将不胜感激。
考虑使用字典映射:
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
}
那
呢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!')
为什么不使用正则表达式?
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
将是一个包含一个或两个元素的列表,用于提供的示例。