我之所以来到这里,是因为这个问题的标题很宽泛,我一直在寻找可用于匹配特定日期格式(例如OP)的正则表达式。但是后来我发现,正如许多答案和评论已全面突出显示的那样,当提取与质量低劣或非结构化源数据混在一起的日期时,有许多陷阱使构建有效模式变得非常棘手。
在探讨问题时,我想出了一个系统,使您可以通过将四个在分隔符上匹配的更简单的子表达式以及顺序中的年,月和日字段的有效范围排列在一起,来构建正则表达式您需要。
这些是 :-
测力计
[^\w\d\r\n:]
这将匹配不是单词字符,数字字符,回车符,换行符或冒号的任何内容。冒号必须在那儿以防止在类似于日期的时间匹配(请参阅我的测试数据)
您可以优化模式的这一部分以加快匹配速度,但这是检测大多数有效定界符的良好基础。
但是请注意;它将匹配带有混合定界符(例如2 / 12-73)的字符串,该分隔符可能实际上不是有效日期。
年值
(\d{4}|\d{2})
这与两位或四位数字匹配,在大多数情况下是可以接受的,但是如果您要处理的是0-999年或9999年以后的数据,则需要决定如何处理,因为在大多数情况下为1,3或> 4位数字的年份是垃圾。
月值
(0?[1-9]|1[0-2])
匹配1到12之间的任何数字,带或不带前导零-注意:0和00不匹配。
日期值
(0?[1-9]|[12]\d|30|31)
匹配1到31之间的任何数字,带或不带前导零-注意:0和00不匹配。
此表达式匹配日期,月份,年份格式的日期
(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})
但它也可以匹配某些年,月日期。还应与边界运算符一起进行预订,以确保选择了整个日期字符串,并防止从格式不正确的数据(即没有边界标签的数据中提取有效的子日期)匹配20/12/194和20/12/19以及101/12/1974比赛为01/12/1974
将下一个表达式的结果与上一个表达式的结果与废话部分中的测试数据进行比较(如下)
\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b
此正则表达式没有验证,因此将匹配格式正确但无效的日期(例如31/02/2001)。这是一个数据质量问题,正如其他人所说的,您的正则表达式不需要验证数据。
由于您(作为开发人员)不能保证源数据的质量,因此您确实需要执行并处理代码中的其他验证,因此,如果尝试匹配和验证RegEx中的数据,它将变得非常混乱,并且变得很难没有非常简洁的文档支持。
垃圾进垃圾出。
话虽如此,如果您确实使用了日期值各不相同的混合格式,则必须尽可能地提取;您可以像这样将两个表达式组合在一起;
此(灾难性)表达式匹配DMY和YMD日期
(\b(0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](0?[1-9]|1[0-2])[^\w\d\r\n:](\d{4}|\d{2})\b)|(\b(0?[1-9]|1[0-2])[^\w\d\r\n:](0?[1-9]|[12]\d|30|31)[^\w\d\r\n:](\d{4}|\d{2})\b)
但是您将无法确定1973年6月9日这样的日期是9月6日还是6月9日。我正在努力思考这样一种情况,即不会在某个地方造成问题,这是不好的做法,您不必这样处理-找到数据所有者并用治理锤来打击他们。
最后,如果要匹配不带分隔符的YYYYMMDD字符串,则可以消除一些不确定性,表达式如下所示
\b(\d{4})(0[1-9]|1[0-2])(0[1-9]|[12]\d|30|31)\b
但请再次注意,它将匹配格式正确但无效的值,例如20010231(2月31日!):)
测试数据
在对该线程中的解决方案进行试验时,我最终得到了一个测试数据集,该数据集包含各种有效和无效日期,以及一些可能需要或不希望匹配的棘手情况,例如,可以作为日期和日期匹配的Times多行。
我希望这对某人有用。
Valid Dates in various formats
Day, month, year
2/11/73
02/11/1973
2/1/73
02/01/73
31/1/1973
02/1/1973
31.1.2011
31-1-2001
29/2/1973
29/02/1976
03/06/2010
12/6/90
month, day, year
02/24/1975
06/19/66
03.31.1991
2.29.2003
02-29-55
03-13-55
03-13-1955
12\24\1974
12\30\1974
1\31\1974
03/31/2001
01/21/2001
12/13/2001
Match both DMY and MDY
12/12/1978
6/6/78
06/6/1978
6/06/1978
using whitespace as a delimiter
13 11 2001
11 13 2001
11 13 01
13 11 01
1 1 01
1 1 2001
Year Month Day order
76/02/02
1976/02/29
1976/2/13
76/09/31
YYYYMMDD sortable format
19741213
19750101
Valid dates before Epoch
12/1/10
12/01/660
12/01/00
12/01/0000
Valid date after 2038
01/01/2039
01/01/39
Valid date beyond the year 9999
01/01/10000
Dates with leading or trailing characters
12/31/21/
31/12/1921AD
31/12/1921.10:55
12/10/2016 8:26:00.39
wfuwdf12/11/74iuhwf
fwefew13/11/1974
01/12/1974vdwdfwe
01/01/99werwer
12321301/01/99
Times that look like dates
12:13:56
13:12:01
1:12:01PM
1:12:01 AM
Dates that runs across two lines
1/12/19
74
01/12/19
74/13/1946
31/12/20
08:13
Invalid, corrupted or nonsense dates
0/1/2001
1/0/2001
00/01/2100
01/0/2001
0101/2001
01/131/2001
31/31/2001
101/12/1974
56/56/56
00/00/0000
0/0/1999
12/01/0
12/10/-100
74/2/29
12/32/45
20/12/194
2/12-73