ホームページ >バックエンド開発 >PHPチュートリアル >日付正規表現の概要
PHP プログラマーとして、私たちは必然的に日付の正規表現に触れることがあります。では、プログラマーとして、日付の正規表現に対する解決策はいくつありますか?この記事では、日付の正規表現について詳しく説明します。
1 概要
日付の正規化は通常、形式要件があり、データがユーザーによって直接入力されない場合に使用されます。アプリケーションシナリオが異なるため、記述される通常のルールも異なり、当然複雑さも異なります。通常の文章では、特定の状況に基づいた詳細な分析が必要です。基本原則は、複雑なものではなく、適切なものだけを書くことです。
日付抽出の場合、非日付と区別できる限り、
\d{4}-\d{2}-\d{2}
のような最も単純な正規表現を書くだけです
yyyy-MM-dd形式の日付がソース文字列内で一意に特定できる場合は、抽出に使用できます。 検証は文字構成や書式だけを検証するだけではあまり意味がなく、ルールの検証も加える必要があります。うるう年の存在により、日付の検証がより複雑になります。 まず、日付の有効範囲とうるう年について調べてみましょう。
2 日付ルール
2.1 日付の有効範囲
2.2 閏年とは
2.3 日付形式
。
yyyy.MM.dd
3
日付正規表現の構築
3.1 ルール分析
すべての年のすべての月には 1 ~ 28 日が含まれます 2 月を除くすべての年には 29 日と 30 日が含まれます 1、3、5、7、8、10、および 12 月のすべての月には 31 日が含まれます 閏年の 2 月には 29 日が含まれます3.1.2 日付に応じて4つのカテゴリーに分けることができます3.1.3 分類方法が に選ばれるのは、日付分類後の実装が (exp1|exp2|exp3) の分岐構造を通じて実現され、分岐構造が左の分岐から右に向かって開始され、When に一致する分岐が試行されるためです。成功した場合は、右への試行は行われません。そうでない場合は、すべての分岐を試行し、失敗を報告します。 分岐の数と各分岐の複雑さはマッチング効率に影響します。検証された日付の確率分布を考慮すると、そのほとんどは 1 ~ 28 日以内に収まるため、2 番目の分類方法を使用するとマッチング効率が効果的に向上します。 。 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("yyyy-MM-dd") + " false"); } dt = dt.AddDays(1); } if (!dateRegex.IsMatch(dt.ToString("yyyy-MM-dd"))){ Console.WriteLine(dt.ToString("yyyy-MM-dd") + " false"); } Console.WriteLine("结束日期: " + dt.ToString("yyyy-MM-dd")); sw.Stop(); Console.WriteLine("测试用时: " + sw.ElapsedMilliseconds + "ms"); Console.WriteLine("测试完成!"); Console.ReadLine();
4 日期正则表达式扩展
4.1 “年月日”形式扩展
以上实现的是yyyy-MM-dd格式的日期验证,考虑到连字符的不同,以及月和日可能为M和d,即yyyy-M-d的格式,可以对以上正则进行扩展
^(?:(?!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)([-/.]?)0?2([-/.]?)29)$
使用反向引用进行简化,年份0001-9999,格式yyyy-MM-dd或yyyy-M-d,连字符可以没有或是“-”、“/”、“.”之一。
^(?:(?!0000)[0-9]{4}([-/.]?)(?:(?:0?[1-9]|1[0-2])\1(?:0?[1-9]|1[0-9]|2[0-8])|(?:0?[13-9]|1[0-2])\1(?:29|30)| (?:0?[13578]|1[02])\1(?:31))|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)([-/.]?)0?2\2(?:29))$
这就是“年月日”这种形式最全的一个正则了,不同含义部分以不同颜色标识,可以根据自己的需要进行栽剪。
4.2 其它形式扩展
了解了以上正则各部分代表的含义,互相间的关系后,就很容易扩展成其它格式的日期正则,如dd/MM/yyyy这种“日月年”格式的日期。
^(?:(?:(?:0?[1-9]|1[0-9]|2[0-8])([-/.]?)(?:0?[1-9]|1[0-2])|(?:29|30)([-/.]?)(?:0?[13-9]|1[0-2])|31([-/.]?) (?:0?[13578]|1[02]))([-/.]?)(?!0000)[0-9]{4}|29([-/.]?)0?2([-/.]?) (?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00))$
这种格式需要注意的就是不能用反向引用来进行优了。连字符等可根据自己的需求栽剪。
4.3 添加时间的扩展
时间的规格很明确,也很简单,基本上就HH:mm:ss和H:m:s两种形式。
([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]
合入到日期的正则中,yyyy-MM-dd HH:mm:ss
^(?:(?!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)\s+([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$
4.4 年份定制
以上所有涉及到平年的年份里,使用的是0001-9999。当然,年份也可以根据闰年规则定制。
如年份1600-9999,格式yyyy-MM-dd或yyyy-M-d,连字符可以没有或是“-”、“/”、“.”之一。
^(?:(?:1[6-9]|[2-9][0-9])[0-9]{2}([-/.]?)(?:(?:0?[1-9]|1[0-2])\1(?:0?[1-9]|1[0-9]|2[0-8])| (?:0?[13-9]|1[0-2])\1(?:29|30)|(?:0?[13578]|1[02])\1(?:31))|(?:(?:1[6-9]|[2-9][0-9]) (?:0[48]|[2468][048]|[13579][26])|(?:16|[2468][048]|[3579][26])00)([-/.]?)0?2\2(?:29))$
5 特别说明
以上正则采用的是最基本的正则语法规则,绝大多数采用传统NFA引擎的语言都可以支持,包括JavaScript、Java、.NET等。
另外需求说明的是,虽然日期的规则相对明确,可以采用这种方式裁剪来得到符合要求的日期正则,但是并不推荐这样使用正则,正则的强大在于它的灵活性,可以根据需求,量身打造最合适的正则,如果只是用来套用模板,那正则也就不称其为正则了。
正则的语法规则并不多,而且很容易入门,掌握语法规则,量体裁衣,才是正则之“道”。
6 应用
一、首先看需求
日期的输入:
手动输入,可输入两种格式yyyymmdd或yyyy-mm-dd
二、解决思路
用户手动输入日期,需要验证输入的日期格式
用户可能的输入情况可以分为以下几种:
(1).输入为空或者为空格
(2).输入非日期格式
根据保存到数据库中的日期格式,保存的格式为yyyy-mm-dd,所以用户在输入yyyymmdd后需要进行转换,转换成yyyy-mm-dd。
思路:
验证日期格式,首现想到的是VS的验证控件,但是因为需要验证的控件有几十个,使用验证控件就需要一个个的拉控件,如果后期需要修改也很麻烦,而通过JS实现控制,再通过正则表达式对日期进行验证。
三、JS实现
//验证日期 function date(id) { var idvalue = document.getElementById(id).value; //通过查找元素 var tmpStr = ""; var strReturn = ""; //调用trim()去掉空格,因为js不支持trim() var iIdNo = trim(idvalue); //正则表达式,判断日期格式,包括日期的界限,日期的格式,平年和闰年 var v = idvalue.match(/^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$/); //输入为空时跳过检测 if (iIdNo.length == 0) { return false; } //自动更改日期格式为yyyy-mm-dd if (iIdNo.length == 8) { tmpStr = iIdNo.substring(0, 8); tmpStr = tmpStr.substring(0, 4) + "-" + tmpStr.substring(4, 6) + "-" + tmpStr.substring(6, 8) document.getElementById(id).value = tmpStr; document.getElementById(id).focus(); } //验证,判断日期格式 if ((iIdNo.length != 8) && !v) { strReturn = "日期格式错误,提示:19990101或1999-01-01"; alert(strReturn); document.getElementById(id).select(); return false; } } //运用正则表达式去除字符串两端空格(因为js不支持trim()) function trim(str) { return str.replace(/(^\s*)|(\s*$)/g, ""); } //前台调用(获得焦点触发) <input class="txtenterschooldate" size="14" type="text" id="txtenterschooldate" name="txtenterschooldate" onblur="date('txtenterschooldate')"/>
以上内容就是关于日期正则表达式的思路详解,如果大家觉得有用那就赶紧收藏起来吧。
相关推荐:
以上が日付正規表現の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。