本系列内容由ZouStrong整理收录
整理自《JavaScript权威指南(第六版)》,《JavaScript高级程序设计(第三版)》
正则表达式是一个描述字符模式的对象,在JavaScript使用RegExp类表示正则表达式
String和RegExp都定义了相关的模式匹配方法
正则表达式使用RegExp对象表示
最简单的创建正则表达式的方式就是使用正则表达式直接量,即包含在一对斜杠(/)之间的字符序列
var expression = /pattern/flags;
其中模式(pattern)可以是任何的正则表达式,每个正则表达式都可以带有修饰符(flags,后述),用于进一步描述正则表达式的行为,他们位于斜杠之外
先看几个例子,感受一下
//可以匹配字符串中所有的"strong"子串
var pattern1 = /strong/g
//可以匹配第一个"Strong"或者"strong"
var pattern2 = /[Ss]trong/
//可以匹配所有以"strong"结尾的7个字符的组合,不区分大小写
var pattern3 = /.strong/gi
创建正则表达式的第二种方式就是使用使用RegExp()构造函数(可以使用字面量定义的任何表达式,都可以使用构造函数来定义)
它接收两个参数
第一个参数,字符串,必须,表示要创建的正则表达式
第二个参数,字符串,可选,表示正则表达式的修饰符
var expression = new RegExp("pattern" , "flags");
如下所示
//匹配第一个"strong"或者"ztrong",不区分大小写
var pattern1 = /[sz]trong/i;
//使用构造函数方式,创建相同正则表达式
var pattern2 = new RegExp("[sz]trong", "i");
使用构造函数创建正则表达式很有用,特别是需要动态创建正则表达式的时候,这种情况没有办法通过写死在代码中的正则表达式直接量来实现
var text = document.getElementById('input').value;
var pattern =new RegExp('[^'+text+']');
使用字面量和使用构造函数创建的正则表达式不一样
在ECMAScript 3 中,正则表达式字面量始终会共享同一个 RegExp 实例,而使用构造函数创建的每一个新 RegExp 实例都是一个新实例
var re = null,
i;
for (i=0; i < 10; i++){
re = /cat/g;
re.test("catastrophe");
}
for (i=0; i < 10; i++){
re = new RegExp("cat", "g");
re.test("catastrophe");
}
在第一个循环中,即使是循环体中指定的,但实际上只为/cat/创建了一个 RegExp 实例。由于实例属性(后面详述)不会重置,所以在循环中再次调用 test() 方法会失败。这是因为第一次调用 test() 找到了"cat" ,但第二次调用是从索引为 3 的字符(上一次匹配的末尾)开始的,所以就找不到它了。由于会测试到字符串末尾,所以下一次再调用 test() 就又从开头开始了
第二个循环使用 RegExp 构造函数在每次循环中创建正则表达式。因为每次迭代都会创建一个新的RegExp 实例,所以每次调用 test() 都会返回 true。
ECMAScript 5 明确规定,使用正则表达式字面量必须像直接调用 RegExp 构造函数一样,每次都创建新的 RegExp 实例(IE9+,及标准浏览器都做出了修改)
如何检测某个对象是不是正则表达式?显然typeof操作符不行(对除了函数以外的所有对象都是返回"Object")
对于同一个网页或者一个全局作用域而言,使用instanceof 运算符即可
reg instanceof RegExp //返回true 或 false
reg.constructor === "RegExp" //true或false
最根本的方法就是检测数组的类特性——"RegExp"
Object.prototype.toString.call(reg).slice(8,-1); //"RegExp"
正则表达式中大多数的字符都是按照其字面意思直接进行匹配的
所有的字母和数字都是按照字面意思进行匹配的
/ab/i 匹配字符串中的第一个"ab"子串,且不区分大小写
/12/g 匹配字符串中的所有"12"子串
正则表达式也支持非字母的字符匹配,但需要使用反斜线(\)进行转义
在正则表达式中,许多标点符号具有特殊含义,称之为元字符
^ $ . * + ? = ! : | / \ ( ) [ ] { }
这些元字符在正则表达式的特定上下文中都有特定用途(后述),因此如果想要匹配这些字符本身,就必须使用" " 进行转义
/[bc]at/i 匹配第一个"bat"或者"cat",不区分大小写
/\[bc\]at/i 匹配第一个"[bc]at",不区分大小写
/.at/gi 匹配所有以"at"结尾的3个字符的组合,不区分大小写
/\.at/gi 匹配所有".at",不区分大小写
由于传递给 RegExp 构造函数的两个参数都是字符串(不能把正则表达式字面量传递给 RegExp 构造函数),所以在某些情况下要对字符进行双重转义。所有元字符都必须双重转义,那些已经转义过的字符也是如此,例如\n(字符\在字符串中通常被转义为\\,而在正则表达式字符串中就会变成\\\\)
字面量形式 RegExp 构造函数
/\[bc\]at/ 等价 "\\[bc\\]at"
/\.at/ 等价 "\\.at"
/name\/age/ 等价 "name\\/age"
/\d.\d{1,2}/ 等价 "\\d.\\d{1,2}"
/\w\\hello\\123/ 等价 "\\w\\\\hello\\\\123"
原则上正则表达式的一个字符对应字符串中的一个字符,使用字符类,则可以让正则表达式中的一个整体对应字符串中的一个字符(多对一)
将直接量字符单独放进方括号内"[ ]"就组成了字符类,字符类可以匹配它所包含的任意一个字符
/[abc]/ 匹配 "a","b","c" 中的任意一个字符
在字符类中,可以通过 "^" 符号来定义否定字符类,只需要将 "^" 符号作为方括号内的第一个字符,他匹配所有不包含在方括号内的字符("^"还有其他含义,后述)
/[^abc]/ 匹配 "a","b","c" 之外的任意一个字符
在字符类中,还可以通过连字符 "-" 表示字符范围
/[a-z]/ 匹配任意一个小写字母
/[A-Z]/ 匹配任意一个大写字母
/[0-9]/ 匹配任意一个0~9的数字
/[a-zA-Z0-9]/ 匹配任意一个字母或者0~9的数字
由于很多字符类经常使用,因此正则表达式提供了一些字符类的简写方式(有些字符类只能匹配ASCII字符,有些可以匹配Unicode字符,但是可以通过十六进制方式显式匹配Unicode字符,例如 /[\u4e00-\u9fa5]/匹配任意中文字符)
需要注意的是,字符类中可以包含字符类(必须是简写的字符类)
/[\s\d]/ 匹配任意一个空白符或者任意一个数
/\s\d/ 匹配任意一个空白符和一个数字组成的字符
我们可能遇到以下情况
/aaaa/ //匹配四个"a"
/\d\d\d\d/ //匹配任意四位数
/[a-z][a-z][a-z]\d/ //匹配三个小写字母和一个数字
这些正则表达式中都出现了重复的元素,显得有点冗余,因此有相应的语法进行简化
使用"?"和"*"时注意,因为这些符号可能匹配0个字符,因此它们允许什么都不匹配,/a*/可以匹配"bbbb",因为它含有0个a
/\d{2,4}/ //匹配2~4位数字
/\w{3}\d?/ //匹配三个单词(数字)和一个可选的数字
/\s*java\s*/ //匹配前后带有零个或多个空格的字符串java
/[^(]+/ //匹配一个或多个非左括号的字符
匹配重复字符是尽可能多的匹配,而且允许后续的正则表达式继续匹配,因此,称之为贪婪的匹配
若想进行非贪婪的匹配,只需在待匹配的字符后再跟随一个问号即可
{1,3}? 或 ?? 或 +? 或 *?
例如 /a+/可以匹配一个或多个连续的字母"a",当使用"aaa"作为匹配字符串时,正则表达式会匹配它的三个字符;/a+?/ 也可以匹配一个或多个连续的字母a,但是它是尽可能少的匹配,正则表达式只会匹配第一个"a"
使用非贪婪的重复可能得到与预期不一致的结果
/a+b/ 和 /a+?b/ 都可以匹配一个或多个a,以及一个b,但是当使用"aaab"作为匹配字符串时,前者和后者都匹配了整个字符串
因为模式匹配总是会寻找字符串中第一个可能匹配的位置,由于该匹配是从字符串的第一个字符开始的,因此不考虑它的子串中更短的匹配
"|"符号,用于分隔供选择的字符,例如
/a|b|c/ 匹配"a","b","c"中的一个
/\d{3}|[a-z]{4}/ 匹配三个数字或者四个小写字母
看起来与字符类相似,但字符类中选择的只能是单个字符,而“|”可以将任意数量字符分隔
/[abc]/ 匹配"a","b","c"中的一个
/a|b|c/ 匹配"a","b","c"中的一个
/aa|bb|cc/ 匹配"aa","bb","cc"中的一个
选择项的匹配次序是从左往右,直到发现了匹配项,如果左边的选择项匹配,就忽略右边的匹配项,即使它产生更好的匹配
/a|ab/ //匹配"ab"时,它只能匹配第一个字符
正则表达式中,"( )"有多种作用
第一个作用就是分组,将独立的正则表达式元素分成一组,以便作为一个独立的整体去处理
/JavaScript?/ 匹配字符串"JavaScrip"或"JavaScript"
/Java(Script)?/ 匹配字符串"Java"或"JavaScript"
第二个作用就是引用,允许在同一个正则表达式的后部引用前面的子表达式
可以通过在字符""后面加一位或多位数字来实现,数字指定了带圆括号的子表达式在正则表达式中的位置,\1引用的就是第一个带圆括号的子表达式,\2引用的就是第二个带圆括号的字表达式........)(因为子表达式可以嵌套另一个子表达式,所以它的位置是参与计数的左括号的位置)
/(Java([Ss]cript)?)/ //\2指代的就是 ([Ss]cript)相匹配的文本
此外,对正则表达式中前一个子表达式的引用,并不是指对子表达式模式的引用,而指的是与那个模式相匹配的文本的引用,这样,可以用于实施一条约束,即一个字符串各个单独部分包含的是完全相同的字符
/['"][^'"]*['"]/ //匹配的是位于引号内的0到多个非引号字符
/(['"])[^'"]*\1/ //匹配的是位于引号内的0到多个非引号字符(左右的引号是匹配的)
正则表达式不允许使用双引号的内容中有单引号,反之亦然,不能在字符类中使用这种引用
/(['"])[^\1]*\1/ //非法的
此外,在JavaScript中,可以仅仅创建分组,但是不会产生带数字编码的引用,即零宽的分组,使用的是——(?: )
/(Java(?:[Ss]cript)?)(fun)/ \2 指代的就是 (fun)匹配的文本
除了匹配实际的字符,有些正则表达式的元素匹配的是字符之间的位置,而不是实际的字符
还记得,使用/\sJava\s/,可以匹配前后都有空格的单词" Java ",但是这样如果"Java"出现在字符串的开始或者结尾,就匹配不成功,并且当我们找到了与之匹配的字符串时,它返回的匹配字符串的前后端都有空格,这并不是想要的结果
\b匹配一个单词的边界(即位于 \w (ASCII单词)字符和\W(非ASCII单词)字符之间的边界,或者位于一个ASCII单词与字符串的开始或者结尾之间的边界)
/\bJava\b/ 匹配"Java" 匹配"%java#"
\b匹配的是边界,[\b]匹配的是退格符
而 \B 将匹配的锚点定位在不是单词的边界之处,因此 /\BScript/ ,与"JavaScript"匹配,但不与"Script"匹配
像 \b 或\B这样的元素,不匹配某个可见的字符,它们指定匹配发生的合法位置,我们称这些元素为正则表达式的锚,因为它们将模式定位在搜索字符串的特定位置上
最常用的锚元素是 "^",用以匹配字符串的开始;锚元素"$"用以匹配字符串的结束
/^JavaScript$/ 匹配单词"JavaScript"
任意正则表达式都可以作为锚点条件,在 (?= 和 ) 之间加入一个表达式,它就是一个零宽先行断言,用以说明圆括号内的表达式必须正确匹配,但并不是真正意义上的匹配(位于 (?= 和 ) 之间之间的表达式只用于指定一个位置,不包含在匹配项中)
比如,要匹配一个单词,但是只在后面有冒号时才匹配
/Java(Script)?(?=\:)/
可以匹配 "JavaScript:The book"中的"JavaScript",但不能匹配"Java the book"中的'Java',因为它后面没有冒号
在 (?! 和 ) 之间加入一个表达式,它就是一个零宽负向先行断言,用以指定接下来的字符都不必匹配
/Java(?!Script)([A-Z]\w*)/
可以匹配"JavaAbc",不能匹配"Javaabc"
可以匹配"JavaScript",不能匹配"JavaScriptEe"
最后需要注意的是,这两种断言都是零宽的,即不产生引用
修饰符用以说明模式匹配的规则,存在于 /..../ 之外
修饰符"i",用以说明模式匹配是不区分大小写的
/Java/i 匹配"Java"、"java"或者"JAVA"...
修饰符"g",用以说明模式匹配是全局的,也就是,找出被检索字符串中的所有匹配,而不是找到第一个之后停止
"aba".replace(/a/ , "c"); "cba"
"aba".replace(/a/g , "c"); "cbc"
修饰符"m",用以说明在多行模式中执行匹配,在这种模式下,如果待检索的字符串包含多行,那么^和$锚字符除了匹配整个字符串的开始和结尾,还将匹配每行的开始和结尾
/Java$/m 匹配"Java",也匹配"Java\nScript"
每个RegExp对象都包含5个属性,通过这些属性可以取得模式的各种信息
lastIndex:可读/可写,整数,如果匹配模式带有修饰符"g",这个属性存储在整个字符串中下一次检索的开始位置(从 0 算起)
var pattern = /a/;
var text = "aaaaa";
for (var i=0; i < 5; i++){
alert(pattern.lastIndex); //输出5次0
pattern.test(text); //因为没有设置"g",所以每次匹配完后,都重头开始
}
和
var pattern = /a/g;
var text = "aaaaa";
for (var i=0; i < 5; i++){
alert(pattern.lastIndex); //输出0,1,2,3,4
pattern.test(text);
}
注意,lastIndex属性是可写的,因此我们可以强制让匹配重新开始
var pattern = /a/g;
var text = "aaaaa";
for (var i=0; i < 5; i++){
pattern.lastIndex = 0;
pattern.test(text); //每次都是从头开始查找
}
RegExp 实例继承的 toLocaleString() 和 toString() 方法都会返回正则表达式的字面量形式的字符串,即使是构造函数创建的
var pattern = new RegExp("\\[bc\\]at", "gi");
alert(pattern.toString()); 返回 "/\[bc\]at/gi"
alert(pattern.toLocaleString()); 返回 "/\[bc\]at/gi"
正则表达式的 valueOf() 方法返回正则表达式本身(同一个对象)
该方法是专门为了捕获分组“( )”而设计的
exec() 接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回 null
返回的数组虽然是 Array 的实例,但包含两个额外的属性
index属性: 表示匹配项在字符串中的位置
input属性: 表示应用正则表达式的字符串
var arr = /c/.exec("abc"); //["c"]
arr.index; //2
arr.input; //"abc"
数组的第一个元素是与整个模式匹配的字符串,其他元素是与模式中的分组(子表达式)匹配的字符串(如果模式中没有分组,则该数组只包含一项)
var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
alert(matches.index); // 0
alert(matches.input); // "mom and dad and baby"
alert(matches[0]); // "mom and dad and baby"
alert(matches[1]); // " and dad and baby"
alert(matches[2]); // " and baby"
对于 exec() 方法而言,即使在模式中设置了"g",它每次也只会返回一个匹配项
注:只不过,在不设置全局标志的情况下,在同一个字符串上多次调用 exec() 将始终返回第一个匹配项的信息。而在设置全局标志的情况下,每次调用 exec() 则都会在字符串中继续查找新匹配项
var pattern = /[a-z]/g;
var text = "abcd";
for (var i=0; i < 4; i++){
alert(pattern.lastIndex); //输出0,1,2,3
alert(pattern.exec(text)); //输出"a","b","c","d"
}
var pattern = /[a-z]/;
var text = "abcd";
for (var i=0; i < 4; i++){
alert(pattern.lastIndex); //输出0,0,0,0
alert(pattern.exec(text)); //输出"a","a","a","a"
}
注:如果exec()没有发现任何匹配结果,它会将lastIndex重置为0,并且从新查找,不管有没有设置"g"
注:在任何时候都可以将lastIndex设置为0,每当在字符串中找最后一个匹配项后,在使用这个RegExp对象开始新的字符串查找之前,都应当将lastIndex设为0
test()接受一个字符串参数。如果包含正则表达式中的一个匹配,则返回true;否则,返回 false
/Java/.test("JavaScript"); //true
在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法非常方便
因此, test()方法经常被用在 if 语句中
var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)){
}
这样其实是不怎么准确的
var text = "0000-00-000000000"
也是匹配的,除非还要明确限制字数或者指定匹配的边界就行了
var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if (text.length == 11&&pattern.test(text)){
}
字符串的模式匹配方法并不会用到lastIndex属性,实际上,String的模式匹配方法只是简单的将lastIndex属性重置为0
但是一个带有修饰符g的正则表达式使用exec()或者test()方法时,要么在每个字符串中找出所有的匹配,以将lastIndex属性自动重值为0,要么显式将lastIndex属性设置为0(当最后一次检索失败时,需要手动设置lastIndex属性)
如果忘记设置lastIndex,那么下一次对新字符串进行检索时,执行检索的起始位置就可能不是字符串的开始位置,而可能是任意位置
当然,如果正则表达式不带有修饰符g,则不必担心
除了重置lastIndex和不设置g之外,还可以将正则表达式的定义放在循环里定义,这样每次检索就生成一个新的正则对象,每一次的lastIndex都是从0开始的了
var pattern = /a/g;
var text = "aaaaa";
for (var i=0; i < 5; i++){
alert(pattern.lastIndex); //0,1,2,3,4
alert(pattern.test(text)); //true,true,true,true,true
}
var pattern = /a/g;
var text = "adddd";
for (var i=0; i < 5; i++){
alert(pattern.lastIndex); //0,1,0,1,0
alert(pattern.test(text)); //true,false,true,false,true
}
String支持四种使用正则表达式的方法(本节只涉及字符串的模式匹配方法,字符串的其它方法后述)
该方法接收一个正则表达式或者字符串参数(会通过RegExp构造函数转换为正则表达式)
返回第一个与之匹配的子串的开始位置,没有匹配,返回-1
"JavaScript".search(/script/i) //4
search()方法总是返回第一个匹配的子串的开始位置,会自动忽略修饰符"g"
search() 方法始终是从字符串开头向后查找模式,如果是传递字符串参数的话,不如使用indexOf()和lastIndexOf(),不仅可以从前往后查找,还可以从后往前,更可以指定开始查找的位置
接收两个参数
第一个参数是一个正则表达式或者字符串(不会转换成正则表达式)
第二个参数是一个字符串(或者一个能返回字符串的函数),表示要替换成的字符串
"JavaScript".replace(/script/i , "Good") //返回"JavaGood"
如果第一个参数是字符串,那么只会替换第一个匹配的子串;要想替换所有匹配的子串,唯一的办法就是提供一个正则表达式,并且指定修饰符"g"(没有"g"的话,同样只是替换匹配的第一个子串)
var text = "sotrong";
var result = text.replace("o", "a"); //"satrong"
result = text.replace(/o/, "a"); //"satrong"
result = text.replace(/o/g, "a"); //""satrang""
前面提到,正则表达式使用圆括号括起来的子表达式是带有从左到右的索引编号的,而且正则表达式会记忆与每个子表达式匹配的文本
如果,在replace()的替换字符串中出现了$加数字,那么replace()将用与指定的子表达式相匹配的文本来替换这两个字符
//一段引用文本,起始于引号,结束于引号
//中间的内容不包含引号
var par = /"([^"]*)"/g;
//用中文引号替换英文引号,同时保持引号之间的内容(保存在$1中)不被修改
text.replace(par,'“$1”');
此外,还有如下规则
通过这些特殊的字符序列,可以使用最近一次匹配结果中的内容
var text = "cat, bat, sat, fat";
result = text.replace(/(.at)/g, "word ($1)");
alert(result); //word (cat), word (bat), word (sat), word (fat)
replace() 方法的第二个参数也可以是一个函数。在只有一个匹配项(即与模式匹配的字符串)的情况下,会向这个函数传递 3 个参数
模式的匹配项
模式匹配项在字符串中的位置
原始字符串
在正则表达式中定义了多个捕获组的情况下,传递给函数的参数依次是模式的匹配项、第一个捕获组的匹配项、第二个捕获组的匹配项……,但最后两个参数仍然分别是模式的匹配项在字符串中的位置和原始字符串
这个函数应该返回一个字符串,表示应该被替换的匹配项使用函数作为 replace() 方法的第二个参数可以实现更加精细的替换操作
function htmlEscape(text){
return text.replace(/[<>"&]/g, function(match, pos, originalText){
switch(match){
case "<":
return "<";
case ">":
return ">";
case "&":
return "&";
case "\"":
return """;
}
});
}
alert(htmlEscape("<p class=\"greeting\">Hello world!</p>"));
//<p class="greeting">Hello world!</p>
该方法本质上与调用 RegExp 的 exec() 方法相同
接收一个正则表达式或者字符串参数(会通过RegExp构造函数将其转换为正则),返回一个由匹配结果组成的数组
如果正则表达式中设置了修饰符"g",则该方法返回的数组包含字符串中所有匹配的结果,否则只有第一次匹配的结果
"javascript".match(/a/); //返回["a"]
"javascript".match(/a/g); //返回["a","a"]
var text = "cat, bat, sat, fat";
var pattern = /.at/;
//与 pattern.exec(text) 相同
var matches = text.match(pattern);
alert(matches.index); //0
alert(matches[0]); //"cat"
alert(pattern.lastIndex); //0
如果是调用 RegExp 对象的 exec() 方法并传递本例中的字符串作为参数,那么也会得到与此相同的数组:数组的第一项是与整个模式匹配的字符串,之后的每一项(如果有)保存着与正则表达式中的捕获组匹配的字符串
当没有设置修饰符"g"时,match()不会进行全局检索,但是仍然会返回一个数组,数组的第一个元素就是匹配的字符串,余下的元素,则是正则表达式中用圆括号括起来的子表达式,如果match()返回一个数组a,则a[0]存放的是完整的匹配,a[1]存放的是与第一个圆括号括起来的表达式相匹配的子串,以此类推
var url = /(\w+):\/\/([\w.]+)\/(\S*)/;
var text = "请看http://www.abc.com/strong"
text.match(url)
返回["http://www.abc.com/strong", "http", "www.abc.com", "strong"]
接收两个参数,基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中,返回该数组
第一个参数,必须,是一个正则表达式或者字符串参数(不会通过RegExp构造函数转换为正则)
第二个参数,可选,是一个数字,用于指定数组的长度
"I am strong".split(" "); //返回["I","am",strong]
"strong".split(""); //返回["s", "t", "r", "o", "n", "g"]
"strong".split(" ") //返回["strong"]
"1, 2, 3, 4, 5".split(/\s*,\s*/) //返回["1", "2", "3", "4", "5"]
var color = "red,blue,green,yellow";
var colors1 = colorText.split(","); //["red", "blue", "green", "yellow"]
var colors2 = colorText.split(",", 2); //["red", "blue"]
var colors3 = colorText.split(/[^\,]+/); //["", ",", ",", ",", ""]
注:通过正则表达式指定的分隔符出现在了字符串的开头或末尾时,返回的数组的第一项(最后一项)是空字符串
/[\u4e00-\u9fa5]/
/[^\x00-\xff]/
/\n\s*\r/
/[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/
/[a-zA-z]+://[^\s]*/
/\d{3}-\d{8}|\d{4}-\{7,8}/
/[1-9][0-9]{4,}/
/[1-9]\d{5}(?!\d)/
/^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/
/([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8])))/
/^[1-9]\d*$/
/^-[1-9]\d*$/
/^-?[1-9]\d*$/
/^[1-9]\d*|0$/
/^-[1-9]\d*|0$/
/^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$/
/^-[1-9]\d*\.\d*|-0\.\d*[1-9]\d*$/