用Python中的正则表达式匹配日期?

Posted

技术标签:

【中文标题】用Python中的正则表达式匹配日期?【英文标题】:Matching dates with regular expressions in Python? 【发布时间】:2012-05-05 17:49:15 【问题描述】:

我知道已经回答了与我类似的问题,但在阅读完这些问题后,我仍然没有我正在寻找的解决方案。

使用 Python 3.2.2,我需要将“月、日、年”与“月”作为字符串进行匹配,“日”是二月不超过 30、31 或 28 的两位数,闰年二月是不超过 29 的两位数。 (基本上是真实有效的日期)

这是我目前所拥有的:

pattern = "(January|February|March|April|May|June|July|August|September|October|November|December)[,][ ](0[1-9]|[12][0-9]|3[01])[,][ ]((19|20)[0-9][0-9])"
expression = re.compile(pattern)
matches = expression.findall(sampleTextFile)

我仍然不太熟悉正则表达式语法,因此我可能在其中包含不必要的字符(逗号和空格的 [,][ ] 感觉像是错误的处理方式),但是当我尝试为了匹配我的示例文本文件中的“1991 年 1 月 26 日”,“匹配”中的项目的打印结果是 ('January', '26', '1991', '19')。

为什么最后会出现额外的“19”?

另外,我可以在我的正则表达式中添加或更改哪些内容,以使我能够正确验证日期?我现在的计划是接受几乎所有的日期,然后通过将日期分组与月份和年份分组进行比较,以查看日期是否应该是 ,然后使用高级构造将它们剔除

任何帮助都将不胜感激,包括对我如何设计正则表达式的建设性批评。

【问题讨论】:

为什么需要使用正则表达式? (现在你有两个问题......) 我相信@Wooble 所指的引用是“有些人在遇到问题时会想“我知道,我会使用正则表达式。”现在他们有两个问题。我倾向于同意。我建议提取一个字符串和 2 个数字(也许用一个简单的正则表达式,但更有可能只是用逗号分割字符串),然后使用 datetime 测试日期是否有效。 感谢您的建议,但这是一个家庭作业,我需要做出一个表达式来匹配日期。 【参考方案1】:

这是一种创建正则表达式以匹配所需格式的任何日期的一种方法(尽管您显然可以调整逗号是否是可选的,添加月份缩写等等):

years = r'((?:19|20)\d\d)'
pattern = r'(%%s) +(%%s), *%s' % years

thirties = pattern % (
     "September|April|June|November",
     r'0?[1-9]|[12]\d|30')

thirtyones = pattern % (
     "January|March|May|July|August|October|December",
     r'0?[1-9]|[12]\d|3[01]')

fours = '(?:%s)' % '|'.join('%02d' % x for x in range(4, 100, 4))

feb = r'(February) +(?:%s|%s)' % (
     r'(?:(0?[1-9]|1\d|2[0-8])), *%s' % years, # 1-28 any year
     r'(?:(29), *((?:(?:19|20)%s)|2000))' % fours)  # 29 leap years only

result = '|'.join('(?:%s)' % x for x in (thirties, thirtyones, feb))
r = re.compile(result)
print result

那么我们有:

>>> r.match('January 30, 2001') is not None
True
>>> r.match('January 31, 2001') is not None
True
>>> r.match('January 32, 2001') is not None
False
>>> r.match('February 32, 2001') is not None
False
>>> r.match('February 29, 2001') is not None
False
>>> r.match('February 28, 2001') is not None
True
>>> r.match('February 29, 2000') is not None
True
>>> r.match('April 30, 1908') is not None
True
>>> r.match('April 31, 1908') is not None
False

您可能会问,这个光荣的正则表达式是什么?

>>> print result
(?:(September|April|June|November) +(0?[1-9]|[12]\d|30), *((?:19|20)\d\d))|(?:(January|March|May|July|August|October|December) +(0?[1-9]|[12]\d|3[01]), *((?:19|20)\d\d))|(?:February +(?:(?:(0?[1-9]|1\d|2[0-8]), *((?:19|20)\d\d))|(?:(29), *((?:(?:19|20)(?:04|08|12|16|20|24|28|32|36|40|44|48|52|56|60|64|68|72|76|80|84|88|92|96))|2000))))

(我最初打算半开玩笑地列举可能的日期,但无论如何,除了四的倍数之外,我基本上都是手写的。)

【讨论】:

谢谢!我仍在浏览您给我的正则表达式,以慢慢剖析和理解各个组件,但我知道最好的方法是将几个月组合在一起,除了名称之外基本上没有其他区别,并将二月与其余的并匹配表达式的另一部分 我说“没有简单的方法”可以让正则表达式检查月份和日期。所以你展示了如何做到这一点......艰难的方式......你,先生,是疯狂的,但这是一种好的精神错乱。 +1!附言我特别喜欢闰年检查器。 pattern = r'(%s) +(%s), *%s' % years 对我来说显示错误.. pattern = '(%s) +(%s), *%s ' % years TypeError: 格式字符串的参数不足 @monkey 是的,不知道这会如何工作......编辑以修复我认为它的意图。 @Dougal 在这个表达式中仍然有不平衡的括号。请更新.. feb = r'(February) +(?:%s|%s)' % ( r'(?:(0 ?[1-9]|1\d|2[0-8]), *%s' % years, # 1-28 any year r'(?:(29), *((?:(?:19 |20)%s)|2000))' %fours) # 仅 29 闰年【参考方案2】:

首先,正如其他人所说,我认为正则表达式不是解决这个问题的最佳选择,而是回答你的问题。通过使用括号,您将字符串分解为几个子组,当您调用函数 findall 时,您将创建一个包含您创建的所有匹配组和匹配字符串的列表。

((19|20)[0-9][0-9])

这是您的问题,正则表达式将匹配整个年份和 19 或 20,具体取决于年份是从 19 还是 20 开始。

【讨论】:

你的括号不平衡。【参考方案3】:

你有这个正则表达式:

pattern = "(January|February|March|April|May|June|July|August|September|October|November|December)[,][ ](0[1-9]|[12][0-9]|3[01])[,][ ]((19|20)[0-9][0-9])"

正则表达式的一个特性是“字符类”。方括号中的字符构成一个字符类。因此[,] 是一个匹配单个字符,(逗号)的字符类。你不妨把逗号。

也许您想让逗号成为可选的?你可以在它后面加上一个问号:,?

您放入括号中的任何内容都会构成“匹配组”。我认为神秘的额外“19”来自一个你不想拥有的匹配组。您可以使用以下语法创建一个不匹配的组:(?:

所以,例如:

r'(?:red|blue) socks'

这将匹配“red socks”或“blue socks”,但不构成匹配组。如果你把它放在简单的括号内:

r'((?:red|blue) socks)'

这将创建一个匹配组,其值为"red socks""blue socks"

我认为,如果您将这些 cmets 应用于您的正则表达式,它将起作用。现在基本上是正确的。

至于根据月份验证日期,这超出了正则表达式的范围。您的模式将匹配 "February 31" 并且没有简单的方法来解决这个问题。

【讨论】:

【参考方案4】:

一个组由括号(...) 标识,它们从左到右计数,最外层在前。您的最终表达式如下所示:

((19|20)[0-9][0-9])

最外面的括号匹配整个年份,里面的括号匹配前两位数字。因此,对于像“1989”这样的日期,两个匹配组将是 1989 年和 19 年。由于您不想要内部组(前两位数),因此您应该改用非捕获组。非捕获组以?: 开头,使用如下:(?:a|b|c)

顺便说一句,有一些关于如何使用正则表达式here的很好的文档。

【讨论】:

【参考方案5】:

Python 有一个日期解析器作为 time 模块的一部分:

import time
time.strptime("December 31, 2012", "%B %d, %Y")

如果日期格式始终相同,您只需要以上内容。

所以,在实际生产代码中,我会编写一个解析日期的正则表达式,然后使用正则表达式的结果来构建一个格式始终相同的日期字符串。

既然您在 cmets 中说这是作业,我将发布另一个答案,其中包含有关正则表达式的提示。

【讨论】:

我需要使用正则表达式,因为这是我正在努力完成的家庭作业 如果您有一个只是日期的字符串,这将创建一个日期对象,但它不像 regex 那样匹配字符串或更大文本中的日期。【参考方案6】:

以下是一些快速的想法:

每个建议你使用正则表达式以外的东西的人都在给你很好的建议。另一方面,学习正则表达式语法总是一个好时机……

方括号中的表达式 -- [...] -- 匹配这些括号内的任何单个字符。所以写[,],它只包含一个字符,和写一个简单的逗号完全一样:,

.findall 方法返回字符串中所有匹配组的列表。一个组由括号标识——(...)——它们从左到右计数,最外层在前。您的最终表达式如下所示:

((19|20)[0-9][0-9])

最外面的括号匹配整个年份,里面的括号匹配前两位数字。因此,对于像“1989”这样的日期,最后两个匹配组将是198919

【讨论】:

告诉他如何解决它,关于不匹配的组。 (?:19|20) 不,我会让你做的。我不确定它是否需要“修复”,因为没有什么“损坏”。我只是想解释一下这种行为。

以上是关于用Python中的正则表达式匹配日期?的主要内容,如果未能解决你的问题,请参考以下文章

python正则表达式匹配中文日期时间

python正则表达式匹配中文日期时间

使用 python 正则表达式匹配日期

正则表达式以匹配西班牙日期

用Python中的正则表达式匹配输入[重复]

Java正则表达式匹配用点分隔的日期