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

时间:2012-04-25 03:31:04

标签: python regex date matching

我知道我的答案有类似的问题,但在阅读完之后我仍然没有找到我正在寻找的解决方案。

使用Python 3.2.2,我需要将“月,日,年”与月份作为字符串进行匹配,2月份的数字不超过30,31或28,闰年则为2月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日”,打印出“匹配”中的项目是('1月','26','1991','19')。

为什么额外的'19'出现在最后?

另外,我可以在正则表达式中添加或更改哪些内容可以让我正确验证日期?我现在的计划是接受几乎所有的日期,然后使用高级结构将它们淘汰出来,比较日期分组与月份和年份分组,看看日期是否应该<31,30,29,28

我将非常感谢任何帮助,包括对我如何设计我的正则表达式的建设性批评。

6 个答案:

答案 0 :(得分:6)

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

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

(我最初的目的是对可能的日期进行简单的列举,但我基本上最终会用手写出除了4的倍数之外的全部内容。)

答案 1 :(得分:2)

以下是一些简单的想法:

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

方括号中的表达式 - [...] - 匹配这些括号内的任何单个字符。因此,编写仅包含单个字符的[,]与编写简单的未加掩盖的逗号完全相同:,

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

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

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

答案 2 :(得分:2)

一个组由括号(...)标识,它们从左到右计数,最外面是第一个。你的最终表达式如下:

  

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

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

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

答案 3 :(得分:1)

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

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

如果日期格式始终相同,以上就是您所需要的。

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

既然你在评论中说这是家庭作业,我会发布另一个答案,提供有关正则表达式的提示。

答案 4 :(得分:1)

你有这个正则表达式:

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'

这将匹配“红色袜子”或“蓝色袜子”,但不会成为匹配组。如果你把它放在普通括号内:

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

这会产生一个匹配组,其值为"red socks""blue socks"

我认为如果您将这些评论应用于正则表达式,它将起作用。现在大部分都是正确的。

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

答案 5 :(得分:0)

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

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

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