正则匹配日期,请高手给翻译一下这条正则什么含义?

http://www.jb51.net/article/2...网站上看到匹配日期的一个正则,表示很疑惑,没看懂,请高手指点。

^(?:(?!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)$ 

匹配结果如下:

clipboard.png

1, ?:是什么意思?
2, ?! 是什么意思?

阅读 4.5k
6 个回答

谢邀,昨天不在,现在刚看到,首先你这个例子中的匹配表达式也太长了,匹配个日期根本不需要这么麻烦,不建议纠结这个表达式,可以找到更好的正则写法,我就你的两点问题做一个回答吧。
1.简单来说,(?:)就是为了分组,但是不捕获下来供正则表达式其他部分使用,就是说不能使用\1\2这种捕获的子模式。
举个例子

var pattern1=/aacdaa/;
var pattern2=/(?:aa|bb)cd\1/;
var pattern3=/(aa|bb)cd\1/;

var str="aacdaa";
console.log(str.match(pattern1)); // ["aacdaa", index: 0, input: "aacdaa"]
console.log(str.match(pattern2)); // null
console.log(str.match(pattern3)); // ["aacdaa", "aa", index: 0, input: "aacdaa"]

第一个匹配到了,没问题,普通的匹配
第二个就没匹配到,因为\1在这里无法识别,是非获取匹配
第三个就匹配到了,因为()是获取性匹配,看后面的输出 数组中第二项是aa,匹配了这个子模式,可以供后续使用,后面的\1就是匹配到的子模式aa。

所以说(?:)的作用就是为了分组,它和()的区别在上面这个例子中已经表现出来了,至于它和普通模式的区别,就体现在分组的便利上。
上面的例子改一下:

var pattern4=/aacd|bbcd/;
var pattern5=/(?:aa|bb)cd/;

两种写法是一样的匹配模式,但是用了分组之后,简洁了不少,这就是非获取匹配的最常用的作用

2.(?!p)是负向先行断言,意思是要求接下来的字符不与p表达式匹配,,还有一个(?=p)也一块说了吧,这个正好和?!相反,称为正向先行断言,要求接下来的字符与p表达式匹配。也就是说这里的(?:)和(?!)起的就是一个条件作用,用来判断前面的表达式满足不满足p这个条件,只有满足了才匹配,不满足就不匹配。
同样用上面类似的例子来说明:

var pattern6=/aa(?=cd)/;
var pattern7=/aa(?!cd)/;
var s1="aacd";
var s2="aabb";
console.log(s1.match(pattern6),s1.match(pattern7));//["aa", index: 0, input: "aacd"] , null
console.log(s2.match(pattern6),s2.match(pattern7));//null , ["aa", index: 0, input: "aabb"]

s1匹配了pattern6,因为s1中的aa后面接着的是cd,s2匹配了pattern7,因为s1中的aa后面接着的不是cd。

推荐一个理解正则表达式非常好的网站,https://regexper.com/
楼主可以根据生成的图片,一点点去理解正则表达式里面的意思,至于其他的,就不展开了,下面展示了部分图片

clipboard.png

^(?:(?!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)里面的(?!)是断言,这里的断言前面啥都没有……这个嘛……估计是去掉0000这个情况匹配0001到9999年

  • (?:(?:0[1-9]|1[0-2])这里无非就是01到09月和10,11,12月

  • (?:0[1-9]|1[0-9]|2[0-8])这个是01到28日的情况

(?: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)
分三类,分别对应前面说的01到28,30号,具有31号的月份

(?:[0-9]{2}(?:0[48]|2468|13579)|(?:0[48]|2468|13579)00)-02-29)
这里是相当于列出所有的闰年

说句实话,我觉得整个正则里面最后列闰年这个比较有参考价值,其他的看起来也真是够烦的。日期分隔线可能是-,可能是~可能是/或者是年月日,通用性还是一般(比如现在需求把-分隔变成/分隔,改死你)。另外所有的[0-9]其实都可以用d替代,前后加了^和$然后另外还要上一个($:)也非常奇怪

?: 匹配,但是不捕获结果。也不分组
?: 正向否定,意思就是后面绝对不能出现 0000

?:后面的括号不做到匹配结果里
?!不以什么什么开头

匹配日期要这么搞??话说php对这个正则匹配的性能比较差的

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题