我有一个包含多个日期值的字符串,我想全部解析它们。字符串是自然语言,所以到目前为止我发现的最好的事情是dateutil。
不幸的是,如果字符串中包含多个日期值,则dateutil会抛出错误:
>>> s = "I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928"
>>> parse(s, fuzzy=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/pymodules/python2.7/dateutil/parser.py", line 697, in parse
return DEFAULTPARSER.parse(timestr, **kwargs)
File "/usr/lib/pymodules/python2.7/dateutil/parser.py", line 303, in parse
raise ValueError, "unknown string format"
ValueError: unknown string format
有关如何解析长字符串中的所有日期的任何想法?理想情况下,会创建一个列表,但如果需要,我可以自己处理。
我正在使用Python,但此时,如果他们完成工作,其他语言可能还可以。
PS - 我想我可以在中间递归分割输入文件并尝试再试一次,直到它工作,但这是一个黑客的地狱。
答案 0 :(得分:16)
看一下,最简单的方法是修改dateutil parser以获得模糊多项选项。
parser._parse
获取您的字符串,使用_timelex
对其进行标记,然后将令牌与parserinfo
中定义的数据进行比较。
Here,如果令牌与parserinfo
中的任何内容都不匹配,则除非fuzzy
为True,否则解析将失败。
我建议您在没有任何处理时间令牌时允许不匹配,然后当您遇到不匹配时,在该点处理解析后的数据并再次开始寻找时间令牌。
不应该花太多精力。
<强>更新强>
当你在等待你的补丁进入时......
这有点hacky,在库中使用非公共函数,但不需要修改库,也不是反复试验。如果您有任何可以变成浮点数的单独令牌,则可能会出现误报。您可能需要更多地过滤结果。
from dateutil.parser import _timelex, parser
a = "I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928"
p = parser()
info = p.info
def timetoken(token):
try:
float(token)
return True
except ValueError:
pass
return any(f(token) for f in (info.jump,info.weekday,info.month,info.hms,info.ampm,info.pertain,info.utczone,info.tzoffset))
def timesplit(input_string):
batch = []
for token in _timelex(input_string):
if timetoken(token):
if info.jump(token):
continue
batch.append(token)
else:
if batch:
yield " ".join(batch)
batch = []
if batch:
yield " ".join(batch)
for item in timesplit(a):
print "Found:", item
print "Parsed:", p.parse(item)
收率:
Found: 2011 04 23 Parsed: 2011-04-23 00:00:00 Found: 29 July 1928 Parsed: 1928-07-29 00:00:00
更新Dieter
Dateutil 2.1似乎是为了与python3兼容而编写的,并使用名为six
的“兼容性”库。有些东西是不对的,它不会将str
个对象视为文本。
如果您将字符串作为unicode或类文件对象传递,则此解决方案适用于dateutil 2.1:
from cStringIO import StringIO
for item in timesplit(StringIO(a)):
print "Found:", item
print "Parsed:", p.parse(StringIO(item))
如果要在parserinfo上设置选项,请实例化parserinfo并将其传递给解析器对象。 E.g:
from dateutil.parser import _timelex, parser, parserinfo
info = parserinfo(dayfirst=True)
p = parser(info)
答案 1 :(得分:5)
当我离线时,我对昨天发布的答案感到困扰。是的,它完成了这项工作,但它不必要地复杂且极其低效。
这是封底版本应该做得更好!
import itertools
from dateutil import parser
jumpwords = set(parser.parserinfo.JUMP)
keywords = set(kw.lower() for kw in itertools.chain(
parser.parserinfo.UTCZONE,
parser.parserinfo.PERTAIN,
(x for s in parser.parserinfo.WEEKDAYS for x in s),
(x for s in parser.parserinfo.MONTHS for x in s),
(x for s in parser.parserinfo.HMS for x in s),
(x for s in parser.parserinfo.AMPM for x in s),
))
def parse_multiple(s):
def is_valid_kw(s):
try: # is it a number?
float(s)
return True
except ValueError:
return s.lower() in keywords
def _split(s):
kw_found = False
tokens = parser._timelex.split(s)
for i in xrange(len(tokens)):
if tokens[i] in jumpwords:
continue
if not kw_found and is_valid_kw(tokens[i]):
kw_found = True
start = i
elif kw_found and not is_valid_kw(tokens[i]):
kw_found = False
yield "".join(tokens[start:i])
# handle date at end of input str
if kw_found:
yield "".join(tokens[start:])
return [parser.parse(x) for x in _split(s)]
使用示例:
>>> parse_multiple("I like peas on 2011-04-23, and I also like them on easter and my birthday, the 29th of July, 1928")
[datetime.datetime(2011, 4, 23, 0, 0), datetime.datetime(1928, 7, 29, 0, 0)]
值得注意的是,在处理空/未知字符串时,它的行为略微偏离dateutil.parser.parse
。 Dateutil将返回当天,而parse_multiple
返回一个空列表,恕我直言,这是人们所期望的。
>>> from dateutil import parser
>>> parser.parse("")
datetime.datetime(2011, 8, 12, 0, 0)
>>> parse_multiple("")
[]
P.S。刚发现MattH's updated answer做了类似的事情。
答案 2 :(得分:0)
我认为如果你把“单词”放在一个数组中,它应该可以解决问题。有了它,您可以验证它是否是日期,或者放入变量。
获得日期后,您应使用datetime library库。
答案 3 :(得分:0)
为什么不编写一个正则表达式模式,涵盖日期可以出现的所有可能形式,然后启动正则表达式来探索文本?我认为在字符串中用日期来表达日期并不是十几种。
唯一的问题是收集日期的最大表达式