Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何优化正则表达式以查找日/月/年格式的日期?_Python_Python 3.x - Fatal编程技术网

Python 如何优化正则表达式以查找日/月/年格式的日期?

Python 如何优化正则表达式以查找日/月/年格式的日期?,python,python-3.x,Python,Python 3.x,我目前正在阅读《自动化无聊的东西》一书,这是第7章的第一个实际问题: 编写一个正则表达式,可以检测DD/MM/YYYY格式的日期。假设天数从01到31,月份从01到12,年份从1000到2999。请注意,如果日期或月份是个位数,则它将有一个前导零 我想出了这个正则表达式,但我相信它可能比这个暴行简单得多: r"^((([0-2]{1})\d{1})|([3][0,1]))\/(([0]{1}\d{1})|([1][0-2]))\/([1,2]\d{3})$" “暴行”起作用,而且可能也相当有效

我目前正在阅读《自动化无聊的东西》一书,这是第7章的第一个实际问题:

编写一个正则表达式,可以检测DD/MM/YYYY格式的日期。假设天数从01到31,月份从01到12,年份从1000到2999。请注意,如果日期或月份是个位数,则它将有一个前导零

我想出了这个正则表达式,但我相信它可能比这个暴行简单得多:

r"^((([0-2]{1})\d{1})|([3][0,1]))\/(([0]{1}\d{1})|([1][0-2]))\/([1,2]\d{3})$"
“暴行”起作用,而且可能也相当有效。编写可读代码可能需要@Thomas所建议的内容——提取2、2、4位数字,然后进行范围检查

不过,您试图解决的练习是关于正则表达式的,所以我想这一暴行确实是您的答案。在实际情况下,我会使用它,但一定要记录我正在做的事情,可能会使用一个变量来存储正则表达式:

date_extract_re = re.compile("r"^((([0-2]{1})\d{1})|([3][0,1]))\/(([0]{1}\d{1})|([1][0-2]))\/([1,2]\d{3})$")

甚至是一条代码注释来说明我在做什么。

您的表达式的指定过度了

[0-2]
之后不需要
{1}
,因为它已经默认为匹配单个字符;这正是
[…]
的定义。同样地,
[3]
也不是必需的;单个字符
3
也匹配

[0,1]
是一个错误,因为它将匹配任何单个字符
0
1
——该逗号不应存在

在带括号的组中,使用OR
|
条时不需要添加更多括号:
(([0-2])\d)|([3][01])
可以是
([0-2]\d | 3[01])
。结束部分也不需要括号

您不需要转义
/
字符

有了这些变化,你最终会

r"^([0-2]\d|3[01])/(0\d|1[0-2])/[12]\d{3}$"
但是:它也匹配
00/00/2020
,因为
[0-2]\d
(天)和
0\d
(月)中的
\d
也可能是零。若要修复,请将
\d
更改为
[1-9]

r"^([0-2][1-9]|3[01])/(0[1-9]|1[0-2])/[12]\d{3}$"
但这仍然与不存在的日期相匹配,例如
31/04/2020
(4月31日)和
30/02/2020
(2月30日)。一个更隐蔽的案例是2019年2月29日(2019年2月29日)

在一系列的月份里剔除糟糕的天数是可能的,但最终你会遇到闰年的问题。这仍然可以用纯正则表达式“解决”,但必须包含一个长长的闰年值列表

让我们再仔细看看问题陈述:

假设天数从01到31,月份从01到12,年份从1000到2999。请注意,如果日期或月份是个位数,则它将有一个前导零

因此,它真正需要的是:

r"\d{2}/\d{2}/\d{4}$"
因为不要求验证日期。也就是说,例如,您的代码验证了day部分不是
99
——但您可以假设这永远不会发生


如上所述,验证日期最好使用实际代码,而不是正则表达式。

我不会在正则表达式中对范围进行编码,只是在之后检查它。然后就是
r'^\d{2}/\d{2}/\d{4}$'
。如果你像这样排除范围中不需要的部分[^32-99],而不是定义实际的范围,那该怎么办呢。@我用方括号看不到这种范围。下面是regex101所说的:3匹配字符3(区分大小写)1-9在1(索引49)和9(索引57)(区分大小写)之间的单个字符9匹配字符9(区分大小写)@Meto:那么它将完全停止工作。格雷普不做计算。您的表达式的意思是“不是以下字符集中的一个:
23456789
”(它包含一些重复字符,但这些将被消除)。@Austin这正是我要说的:使用正则表达式进行解析,然后使用其他代码检查范围。比试图把所有东西塞进一个怪物正则表达式要简单得多,可读性也更高。谢谢你这么详细的回答!我只是想挑战自己,看看是否有可能在一个简单的正则表达式中验证日期。关于闰年和4月、6月、9月、11月,这是任务的第二部分,必须在不使用正则表达式的情况下完成。