当前位置:  编程技术>其它

正则应用之 日期正则表达式

    来源: 互联网  发布时间:2014-10-15

    本文导语:  1概述 首先需要说明的一点,无论是Winform,还是Webform,都有很成熟的日历控件,无论从易用性还是可扩展性上看,日期的选择和校验还是用日历控件来实现比较好。 前几天在CSDN多个版块看到需要日期正则的帖子,所以整理了...

1概述
首先需要说明的一点,无论是Winform,还是Webform,都有很成熟的日历控件,无论从易用性还是可扩展性上看,日期的选择和校验还是用日历控件来实现比较好。
前几天在CSDN多个版块看到需要日期正则的帖子,所以整理了这篇文章,和大家一起讨论交流,如有遗漏或错误的地方,还请大家指正。
日期正则一般是对格式有要求,且数据不是直接由用户输入时使用。因应用场景的不同,写出的正则也不同,复杂程度也自然不同。正则的书写需要根据具体情况具体分析,一个基本原则就是:只写合适的,不写复杂的。
对于日期提取,只要能与非日期区分开,写最简单的正则即可,如
代码如下:

d{4}-d{2}-d{2}

如果可以在源字符串中唯一定位yyyy-MM-dd格式的日期,则可用做提取。
对于验证,如果仅仅是验证字符组成及格式是没有多大意义的,还要加入对规则的校验。由于闰年的存在,使得日期的校验正则变得比较复杂。
先来考察一下日期的有效范围以及什么是闰年。
2 日期的规则
2.1 日期的有效范围
对于日期的有效范围,不同的应用场景会有所不同。
MSDN中定义的DateTime对象的有效范围是:0001-01-01 00:00:00到9999-12-31 23:59:59。
UNIX时间戳的0按照ISO 8601规范为 :1970-01-01T00:00:00Z。
而实际应用中,日期的范围基本上不会超出DateTime所规定的范围,所以正则验证取其中常用的日期范围即可。
2.2 什么是闰年
(以下摘自百度百科)
闰年(leap year)是为了弥补因人为历法规定造成的年度天数与地球实际公转周期的时间差而设立的。补上时间差的年份为闰年。
地球绕日运行周期为365天5小时48分46秒(合365.24219天),即一回归年(tropical year)。公历的平年只有365日,比回归年短约0.2422 日,每四年累积约一天,把这一天加于2月末(即2月29日),使当年时间长度变为366日,这一年就为闰年。
需要注意的是,现在的公历是根据罗马人的“儒略历”改编而得。由于当时没有了解到每年要多算出0.0078天的问题,从公元前46年,到16世纪,一共累计多出了10天。为此,当时的教皇格雷果里十三世,将1582年10月5日人为规定为10月15日。并开始了新闰年规定。即规定公历年份是整百数的,必须是400的倍数才是闰年,不是400的倍数的就是平年。比如,1700年、1800年和1900年为平年,2000年为闰年。此后,平均每年长度为365.2425天,约4年出现1天的偏差。按照每四年一个闰年计算,平均每年就要多算出0.0078天,经过四百年就会多出大约3天来,因此,每四百年中要减少三个闰年。闰年的计算,归结起来就是通常说的:四年一闰;百年不闰,四百年再闰。
2.3 日期的格式
根据不同的语言文化,日期的连字符会有所不同,通常有以下几种格式:
yyyyMMdd
yyyy-MM-dd
yyyy/MM/dd
yyyy.MM.dd
3 日期正则表达式构建
3.1 规则分析
写复杂正则的一个常用方法,就是先把不相关的需求拆分开,分别写出对应的正则,然后组合,检查一下相互的关联关系以及影响,基本上就可以得出对应的正则。
按闰年的定义可知,日期可以有几种分类方法。
3.1.1 根据天数是否与年份有关划分为两类
与年份无关的一类中,根据每月天数的不同,又可细分为两类
Ø 1、3、5、7、8、10、12月为1-31日
Ø 4、6、9、11月为1-30日
与年份有关的一类中
Ø 平年2月为1-28日
Ø 闰年2月为1-29日
3.1.2 根据包含日期不同可划分为四类
Ø 所有年份的所有月份都包含1-28日
Ø 所有年份除2月外都包含29和30日
Ø 所有年份1、3、5、7、8、10、12月都包含31日
Ø 闰年2月包含29日
3.1.3 分类方法选择
因为日期分类之后的实现,是要通过(exp1|exp2|exp3)这种分支结构来实现的,而分支结构是从左侧分支依次向右开始尝试匹配,当有一个分支匹配成功时,就不再向右尝试,否则尝试所有分支后并报告失败。
分支的多少,每个分支的复杂程度都会影响匹配效率,考虑到被验证日期概率分布,绝大多数都是落到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

    
 
 

您可能感兴趣的文章:

  • Perl 正则表达式之角色化记忆
  • js正则表达式之RegExp对象之compile方法 编译正则表达式
  • Linux c++ boost库正则表达式用法
  • 正则表达式 表示 非指定字符串开头的正则
  • Python通过正则表达式获取,去除(过滤)或者替换HTML标签的几种方法
  • 向大家推荐一个收集整理正则表达式的网站 iis7站长之家
  • linux bash shell命令:文本搜索工具grep正则表达式元字符集(基本集)
  • 正则表达式概述 什么是正则表达式 .
  • JS 正则表达式的相关方法(正则学习笔记1)
  • jQuery中的正则表达式分析 正则基础
  • java 正则表达式基础,实例学习资料收集大全 原创
  • 哪些命令可以使用正则表达式
  • 常用正则表达式及评注-学习正则必备
  • (菜鸟飞飞)问个正则表达式的问题
  • 向大家推荐一个收集整理正则表达式的网站
  • 正则表达式的问题
  • 关于正则表达式匹配问题
  • Python 匹配任意字符(包括换行符)的正则表达式写法
  • php 正则 不包含某字符串的正则表达式
  • Java正则表达式 reb4j
  • 大虾,请问谁有正则表达式的资料?谢谢!
  • java使用正则表达校验手机号码示例(手机号码正则)
  • 寻求正则表达试
  • 正则表达式中使用变量赋值
  • 用正则表达式来表示中文
  • java正则表达式验证函数
  • linux下有什么函数可以处理正则表达式?
  • emacs里空行的正则表达式如何写?
  • 关于sed的正则表达式
  • 正则表达式小疑问
  • killall 正则表达式用法
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • c#正则过滤图片标签 asp.net正则过滤的例子
  • 正则匹配后面非指定字符的正则 原创
  • PHP html标签正则替换并可自定义正则规则
  • python正则表达式去掉数字中的逗号(python正则匹配逗号)
  • 正则表达式口诀_学习正则的朋友值得一看
  • Javascript里的两种使用正则的方法
  • 常用正则 常用的C#正则表达式
  • asp.net正则表达式提取中文的代码示例
  • 正则式 ^[^ ](.*[^ ])?$ 的含义
  • 正则式如何只匹配一个汉字?
  • 正则表达式 口诀 学习正则的朋友看看
  • php使用正则过滤js脚本代码实例
  • shell ip 正则表达式
  • 让URL只允许一些字符的正则表达式
  • 关于通配符和正则表达式
  • 100分。关于字符串的正则表达式。
  • 正则表达式,相关链接
  • PHP正则匹配图片并给图片加链接详解
  • 正则表达式的一个小问题!!!
  • shell脚本中判断字符串匹配正则式的问题




  • 特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3