,就不再向右尝试,否则尝试所有分支后并报告失败。
分支的多少,每个分支的复杂程度都会影响匹配效率,考虑到被验证日期概率分布,绝大多数都是落到1-28日内,所以采用第二种分类方法,会有效提高匹配效率。
3.2 正则实现
采用3.1.2节的分类方法,就可以针对每一个规则写出对应的正则,以下暂按MM-dd格式进行实现。
先考虑与年份无关的前三条规则,年份可统一写作
复制代码 代码如下:
(?!0000)[0-9]{4}
下面仅考虑月和日的正则
Ø 包括平年在内的所有年份的月份都包含1-28日
复制代码 代码如下:
(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])
Ø 包括平年在内的所有年份除2月外都包含29和30日
复制代码 代码如下:
(0[13-9]|1[0-2])-(29|30)
Ø 包括平年在内的所有年份1、3、5、7、8、10、12月都包含31日
复制代码 代码如下:
(0[13578]|1[02])-31)
合起来就是除闰年的2月29日外的其它所有日期
复制代码 代码如下:
(?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)
接下来考虑闰年的实现
Ø 闰年2月包含29日
这里的月和日是固定的,就是02-29,只有年是变化的。
可通过以下代码输出所有的闰年年份,考察规则
复制代码 代码如下:
for (int i = 1; i < 10000; i++)
{
if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0)
{
richTextBox2.Text += string.Format("{0:0000}", i) + "\n";
}
}
根据闰年的规则,很容易整理出规则,四年一闰;
复制代码 代码如下:
([0-9]{2}(0[48]|[2468][048]|[13579][26])
百年不闰,四百年再闰。
复制代码 代码如下:
(0[48]|[2468][048]|[13579][26])00
合起来就是所有闰年的2月29日
复制代码 代码如下:
([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)
四条规则都已实现,且互相间没有影响,合起来就是所有符合DateTime范围的日期的正则
复制代码 代码如下:
^((?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)|([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)$
考虑到这个正则表达式仅仅是用作验证,所以捕获组没有意义,只会占用资源,影响匹配效率,所以可以使用非捕获组来进行优化。
复制代码 代码如下:
^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$
以上正则年份0001-9999,格式yyyy-MM-dd。可以通过以下代码验证正则的有效性和性能
复制代码 代码如下:
DateTime dt = new DateTime(1, 1, 1);
DateTime endDay = new DateTime(9999, 12, 31);
Stopwatch sw = new Stopwatch();
sw.Start();
Regex dateRegex = new Regex(@"^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$");
//Regex dateRegex = new Regex(@"^((?!0000)[0-9]{4}-((0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31)|([0-9]{2}(0[48]|[2468][048]|[13579][26])|(0[48]|[2468][048]|[13579][26])00)-02-29)$");
Console.WriteLine("开始日期: " + dt.ToString("yyyy-MM-dd"));
while (dt <= endDay)
{
if (!dateRegex.IsMatch(dt.ToString("yyyy-MM-dd")))
{
Console.WriteLine(dt.ToString("yyy