1. 什么是正则表达式
- 正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),可以用来描述和匹配字符串的特定模式。
- 正则表达式是一种用于模式匹配和搜索文本的工具。
- 正则表达式提供了一种灵活且强大的方式来查找、替换、验证和提取文本数据。
- 正则表达式可以应用于各种编程语言和文本处理工具中,如 JavaScript、Python、Java、Perl 等。
2. 正则表达式的声明(创建)方式
2.1 字面量声明(创建)
字面量创建是最简便的方式.。
声明(创建)语法: var/let 变量名 = /文本内容/修饰符;
let reg = /a/g;
2.2 实例化声明(创建)
声明(创建)语法:var/let 变量名= new RegExp("文本内容","修饰符");
let reg1=new RegExp("a","g")
3.正则表达式的修饰符
正则表达式是只匹配一个字符。
- g : 全局搜索。
- i :不区分大小写搜索。
- m :多行搜索。
- s : 允许
.
匹配换行符。- u : 使用 unicode 码的模式进行匹配。
- y : 执行“粘性 (
sticky
)”搜索,匹配从目标字符串的当前位置开始。修饰符可以多个一起使用。
replace()是替换方法。
案例:
var str="abAc";
//这里的replace是普通的字符串方法
str=str.replace("a","0");
console.log(str);//0bAc
//这里是使用了正则表达式,查找字符串中有没有字符a,有则替换成字符0,只替换一个
str=str.replace(/a/,"0");
console.log(str);//0bAc
//这里是使用了正则表达式,查找字符串中所有的字符a,有则替换成字符0,全局替换
str=str.replace(/a/g,"0");
console.log(str);//0bAc
//这里是使用了正则表达式,查找字符串中所有的字符a,有则替换成字符0,全局替换并且不区分大小写
str=str.replace(/a/gi,"0");
console.log(str);//0b0c
4. 正则表达式的API(属性+方法)
4.1 正则表达式相关的属性
- flags :获取正则表达式使用到的修饰符。返回值是修饰符。
- source :获取到正则的内容。返回值是文本内容。
- lastIndex :获取最后一次查找的下标。返回值是索引下标。
- unicode :获取正则表达式是否使用的编码。返回值是true或false。
- global :正则表达式是否使用了修饰符g。返回值是true或false。
- ignoreCase :正则表达式是否使用了修饰符i。返回值是true或false。
- multiline :正则表达式是否使用了修饰符m。返回值是true或false。
- dotAll :正则表达式是否使用了修饰符s。返回值是true或false。
- sticky :正则表达式是否使用了修饰符y。返回值是true或false。
案例:
var reg = /a/gim;
console.log(reg.flags);//修饰符
console.log(reg.source);//正则内容
console.log(reg.global);//g
console.log(reg.ignoreCase);//i
console.log(reg.lastIndex);//最后一次查找的下标
console.log(reg.dotAll);
console.log(reg.multiline);//m
console.log(reg.sticky);//粘性
console.log(reg.unicode);//编码
-----------------------------------------
答案:
var reg = /a/gim;
console.log(reg.flags);//"gim"
console.log(reg.source);//"a"
console.log(reg.global);//true
console.log(reg.ignoreCase);//true
console.log(reg.lastIndex);//最后一次查找的下标0
console.log(reg.dotAll);//false
console.log(reg.multiline);//true
console.log(reg.sticky);//false
console.log(reg.unicode);//false
4.2 修饰符与正则属性的对应关系
标志 | 描述 | 对应属性 |
---|---|---|
d | 生成子串匹配的索引。 | hasIndices |
g | 全局查找。 | global |
i | 忽略大小写查找。 | ignoreCase |
m | 允许 ^ 和 $ 匹配换行符。 | multiline |
s | 允许 . 匹配换行符。 | dotAll |
u | “Unicode”;将模式视为 Unicode 码位序列。 | unicode |
v | 升级 u 模式,提供更多 Unicode 码特性。 | unicodeSets |
y | 执行“粘性(sticky)”搜索,从目标字符串的当前位置开始匹配。 | sticky |
4.3 正则的元字符
元字符(Metacharacter)是拥有特殊含义的字符。
- . :查找单个字符,除了换行和行结束符。
- \w :查找数字、字母及下划线。
- \W :查找非英文单词字符。
- \d :查找数字。
- \D:查找非数字字符。
- \s:查找空白字符。
- \S:查找非空白字符。
- \b:匹配单词边界。
- \B:匹配非单词边界。
- \0 :查找 NULL 字符。
- \n : 查找换行符。
- \f :查找换页符。
- \r:查找回车符。
- \t :查找制表符。
- \v :查找垂直制表符。
- \xxx:查找以八进制数 xxx 规定的字符。
- \xdd:查找以十六进制数 dd 规定的字符。
- \uxxxx:查找以十六进制数 xxxx 规定的 Unicode 字符。
- . 通配符元字符:表示匹配任意一个字符。
- \ : 转移符号。
- [ 字符串 ] :表示匹配[ ]内的任意一个字符。 虽然在[ ]中部分具有一定含义的字符会转义,但是建议大家还是用\转义。
- [.] :代表字符. 不再是任意字符。
案例1:
// 正则的元字符
var str="abcacdade";
var reg=/ac/g;
// . 通配符元字符 代表任意一个字符
var reg=/a../g;
console.log(reg.exec(str));
console.log(reg.exec(str));
console.log(reg.exec(str));
案例2:
var str="abacad";
console.log(str.match(/a[bd]/g));
console.log(str.match(/[ab][cd]/g));//ac ad bc bd
4.4 正则表达式相关的方法
4.4.1 正则表达式自身的方法
test() :判断当前字符串中是否有满足该正则匹配的内容,返回值是布尔值。
exec():在字符串中查找满足正则匹配的内容,返回值是数组,数组有对应的属性。
案例:
var reg = /a/gim;
console.log(reg.test("aa"));
console.log(reg.exec("aa"));
test()方法使用全局匹配时的注意事项!!!
在使用test()方法匹配正则时,使用了全局匹配,找到的是第一次第一个满足条件的内容。并且将这个位置的下标记录在正则中。第二次匹配时根据上次记录的下标继续向后匹配查找,直到查找不到返回false,则从索引下标0开始重新查找。
案例1:
// 当使用全局匹配时
var reg=/a/g;
// 012345
console.log(reg.test("cdabac"));//true
console.log(reg.lastIndex);//索引下标为3
console.log(reg.test("cdabac"));//true
console.log(reg.lastIndex);//索引下标为5
console.log(reg.test("cdabac"));//false
console.log(reg.lastIndex);//索引下标为0
console.log(reg.test("cdabac"));//true
console.log(reg.lastIndex);//索引下标为3
console.log(reg.test("cdabac"));//true
console.log(reg.lastIndex);//索引下标为5
console.log(reg.test("cdabac"));//false
console.log(reg.lastIndex);//索引下标为0
案例2:以下这个写法每次都会创建一个新的正则表达式查找。就不会存在向上的案例那样继续根据上次记录的下标继续向后匹配查找的情况。
// 每次都会创建一个新的正则表达式查找
console.log(/a/g.test("cdabac"));//true
console.log(/a/g.test("cdabac"));//true
console.log(/a/g.test("cdabac"));//true
console.log(/a/g.test("cdabac"));//true
console.log(/a/g.test("cdabac"));//true
exec()方法 和 test()方法 使用全局查找时相同。只是 exec()方法没有查找到返回null。
案例:
var reg=/a/g;
console.log(reg.exec("cdabac"));
console.log(reg.exec("cdabac"));
// 没有查找到返回null
console.log(reg.exec("cdabac"));
4.4.2 字符串的方法中可以使用正则表达式的方法
- match(正则表达式) :检索字符串与正则表达式进行匹配的结果。返回值是一个 数组,其内容取决于是否存在全局(
g
)标志,如果没有匹配,则返回 null。- matchAll() :返回一个迭代器,该迭代器包含了检索字符串与 正则表达式 进行匹配的所有结果(包括捕获组)。
- replace() :字符串替换。
- replaceAll() : 替换所有结果。
- search() :查找索引下标,只查找第一个。
- split() :字符串分割。
1. match(正则表达式)专用于正则表达式。
如果不使用g全局匹配类似于exec()方法。
如果使用g全局匹配则会把所有匹配的结果放在一个数组中。
案例:
console.log("abcacd".match(/a../g));
console.log("abcacd".matchAll(/a../g));
2. matchAll()返回一个迭代器,使用for of遍历这个迭代器可以看到多次使用exec()的结果。
for(var value of "abcacd".matchAll(/a../g)){
console.log(value);
}
3. replace() 字符串替换。
案例1:
var str="abcacd";
//这里是普通的字符串查找
console.log(str.replace("a","0"));//0bcacd
//这里是使用了正则表达式查找
console.log(str.replace(/a/,"0"));//0bcacd
console.log(str.replace(/a/g,"0"));//0bc0cd
str=str.replace(/a../g,function(item,index,s){
// 如果下标是3返回”000“
if(index===3) return "000";
// 否则返回原查找到字符
return item;
})
console.log(str);//abc000
-----------------------------------
案例2:
// 将下面str的字符串变成"ab0ac1"
var str="abac";
str=str.replace(/a./g,function(item){
console.log(item);//ab ac
if(item==="ab")return item+0;
return item+1;
})
console.log(str);//ab0ac1
4. replaceAll() 替换所有结果。
var str="abac";
str=str.replaceAll(/a./g,"a0");
console.log(str);//a0a0
str=str.replaceAll(/a./g,function(item){
console.log(item);//a0 a0
});
str=str.replaceAll("a","0")
console.log(str);//undefinedundefined
5. search() 查找索引下标,只查找第一个。
// search 与indexOf类似
//案例1:
var str="abac";
console.log(str.search("a"));//索引下标为0
console.log(str.search(/a/g));//索引下标为0
//案例2:
var str="abac";
var reg=/a/g;
console.log(str.search(reg));//索引下标为0
//案例3:
var str="babd";
console.log(str.search(/.a/g));//索引下标为0
6. split() :字符串分割。
var str="ab|cd|ef&aa&cc";
console.log(str.split("|"));
// 部分字符在正则表达式中有自己的含义,为了恢复这个字符串本身的字符需要再前面加上\ 转义
// \| 这个是字符 |
// | 是或者的意思
// & 就是字符&
console.log(str.split(/\||&/));
5. 正则表达式的模式(方括号+())
方括号用于查找某个范围内的字符。 正则中只有unicode编码相连的才可以使用。
- [abc] :查找方括号之间的任何字符。
- [^abc] :查找任何不在方括号之间的字符。
- [0-9]:查找任何从 0 至 9 的数字。可以使用 \d 代替。
- [^0-9] :查找除了所有数字的字符。可以使用 \D 代替。
- [a-z] : 查找任何从小写 a(97) 到小写 z(123) 的字符。
- [A-Z]:查找任何从大写 A(65) 到大写 Z(93) 的字符。
- [a-zA-Z] :查找全部英文字母字符。
- [a-zA-Z0-9_] :查找小写字母大写字母数字下划线。可以使用 \w 代替。
[^a-zA-Z0-9_]:查找除了小写字母大写字母数字下划线。可以使用 \W 代替。
[^\^]:除了^ 以外的字符。
[a^c] :如果^不在第一位则表示正常的^字符。
- [adgk] :查找给定集合内的任何字符。
- [^adgk]:查找给定集合外的任何字符。
- (red|blue|green):查找任何指定的选项。
注意!!!
以下是错误写法:
- [a-Z] 错误。 正则中只有unicode编码相连的才可以使用。
- [A-z] 错误。 正则中只有unicode编码相连的才可以使用。
- [1-31] 错误。方括号中只能匹配一个字符 [1-31] ---> 1-3 1
案例1:
<body>
<input type="text">
<script>
var input = document.querySelector("input");
input.addEventListener("input", inputHandler);
function inputHandler(e) {
// 只要有\w(小写字母大写字母数字下划线)就是正确
if (/\w/g.test(input.value)) {
input.style.borderColor = "green";
} else {
input.style.borderColor = "red";
}
}
</script>
</body>
案例2:
<body>
<input type="text">
<script>
var input = document.querySelector("input");
input.addEventListener("input", inputHandler);
function inputHandler(e) {
// 如果有非a-z 就会绿色
if (/[^a-z]/g.test(input.value)) {
input.style.borderColor = "green";
} else {
input.style.borderColor = "red";
}
}
</script>
</body>
案例3:
console.log("a".match(/[a^c]/g));//^不是不包含的含义了,所以能找到
console.log("a".match(/[^ac]/g));//^不包含的含义,所以找不到
案例4:
<body>
<input type="text">
<script>
var input = document.querySelector("input");
input.addEventListener("input", inputHandler);
function inputHandler(e) {
input.value = input.value.replace(/[^a-z]/g, "");
}
</script>
</body>
案例5:空白字符可以用于切割。
var str = "i like javascript";
console.log(str.split(" "));
console.log(str.split(/\s/));
console.log(str.match(/\s/g));
案例6:空白字符可以用于切割。
var str = " asdasd asdad asdads asda ";
str = str.replace(/\s/g, "")
console.log(str);
console.log(str.trim());//删除字符串前后空格
console.log(str.trimLeft());//删除字符串前空格
console.log(str.trimRight());//删除字符串前空格
案例7:去掉空格切割
var str = " asdasd asdad asdads asda "
console.log(str.match(/\S/g));
console.log(str.split(""));
案例8:数字
/\d\d\d\d-\d\d-\d\d/ 可以表示 2024-08-24
6. 正则的量词(用于字符串重复)
6.1 量词表
量词 | 描述 |
---|---|
n+ | 匹配任何包含至少一个 n 的字符串。+ 代表 至少有一次 或者 多次。 |
n* | 匹配任何包含零个或多个 n 的字符串。 * 代表 没有 或者 有多次。 |
n? | 匹配任何包含零个或一个 n 的字符串。 ?代表 有 或者 没有。 |
{n} | n是几,就以n个字符为一组的重复。 |
n{X} | 匹配包含 X 个 n 的序列的字符串。 |
n{X,} | X 是一个正整数。前面的模式 n 连续出现至少 X 次时匹配。 |
n{X,Y} | X 和 Y 为正整数。前面的模式 n 连续出现至少 X 次,至多 Y 次时匹配。 |
n$ | 匹配任何结尾为 n 的字符串。 |
^n | 匹配任何开头为 n 的字符串。 |
?=n | 匹配任何其后紧接指定字符串 n 的字符串。 |
?!n | 匹配任何其后没有紧接指定字符串 n 的字符串。 |
6.2 {n} 重复n次详解
案例1:
// /\d{2}/ 数字重复2次
console.log("ab1234".match(/\d{2}/g));
案例2:将"abcdefgh" 变成 ["ab","cd","ef","gf"]。
var str= "abcdefgh"
console.log(str.match(/\w{2}/g));
案例3:将"18617809865"改成 "186 1780 9865"
var str="18617809865";
console.log(str.match(/(\d{3})(\d{4})(\d{4})/));
console.log( str.match(/(\d{3})(\d{4})(\d{4})/).slice(1).join(" "));
注意!!!
- a{1}与/a/相同。
- /a{0}/ 表示a没有,就是表示一个""。
- 每个字符之间有空字符,在字符起始位置和结束位置也有空字符。
- 任何字符{0} 都代表""。
6.3 贪婪匹配重复
/a{n,m}/ : a重复n-m次 ,n是最小重复次数,m是最大重复次数,会优先找最大的。
案例:
console.log("aaaaaa".match(/a{2,5}/g));
console.log("asdasdasdasda".match(/\w{3}/g));
console.log("asdasdasdasda".match(/\w{3,5}/g));
{0,1}:表示最少0次,最多1次,有或者没有都可以。
案例:
//这里表示u匹配0次或1次都可以。换句话说,u可以有也可以没有
console.log(/colou{0,1}r/.test("colour"));//true
console.log(/colou{0,1}r/.test("color"));//true
/a{n,}/ : 表示a至少重复n次,最多无限次。
案例:
console.log("aaaaaaaaa".match(/a{2,}/g));
// 至少1次,可以查找无限次
console.log("aaaaaaaaa".match(/a{1,}/g));
案例:查找一个数字,不知道是第几位
<body>
<input type="text">
<script>
var input = document.querySelector("input");
input.addEventListener("input", inputHandler);
function inputHandler(e) {
if (/[a-zA-Z]{0,}\d/g.test(input.value)) {
input.style.borderColor = "green";
} else {
input.style.borderColor = "red"
}
}
</script>
</body>
案例:
// 希望判断字符串中是否包含小写英文字母
console.log(/[^a-zA-Z]{0,}[a-z]/.test("1231231a23"));//true
// 至少在第1位以后包含英文字符
console.log(/[^a-zA-Z]{1,}[a-z]/.test("123123123"));//false
console.log(/[^a-zA-Z]{1,}[a-z]/.test("a123123123"));//false
console.log(/[^a-zA-Z]{1,}[a-z]/.test("1a23123123"));//true
{0,1} ? 有或者没有
{0,} * 没有或者有多次
{1,} + 至少有一次或者多次
let str = "asdjad asdkja adsakj asdkj";
//表示非空白字符,匹配一次或多次,使用“”替换到多个“”
str = str.replace(/\s+/g, " ")
console.log(str);//"asdjad asdkja adsakj asdkj"
6.4 非贪婪匹配重复
非贪婪匹配:找到对应位置结束的。非贪婪匹配都会有一个结束字符作为控制。
非贪婪匹配的情况一:匹配范围比较大可能包含了后面的内容。
非贪婪匹配的情况二:在?后有一个相对结束对应的字符。
情况一的案例:
var str = "中国的四大古典名著有《西游记》、《水浒》、《三国演义》、《红楼梦》";
//匹配《》中的内容有或者没有
console.log(str.match(/《[^《》]*》/g));
情况二的案例:
var str = "中国的四大古典名著有《西游记》、《水浒》、《三国演义》、《红楼梦》";
//匹配《》中的内容有一个任意的字符或多个任意的字符遇到后》结束
console.log(str.match(/《.*?》/g));
案例:
var str = "asdashd123123csjkdhasd12312312sadasd123123";
//匹配一个或多个字符(大写字母+小写字母+数字),遇到数字则结束。
console.log(str.match(/\w+?\d/g));
案例:
var str = "adasd'bbbb'asdasd'12312313'";
//匹配‘’中一个任意字符或多个字符遇到’则结束
console.log(str.match(/'.+?'/g));
案例:
var str = "2024-02-20";
//匹配非空白字符一次或多次,遇到-则结束
console.log(str.match(/\d+-?/g));
//匹配一个任意字符一次或多次遇到-则结束
console.log(str.match(/.+?-/g));
7. 起始结束和或者(|)
[^] :反义在[ ]中开始叫反义 ,在正则表达式开始叫做起始。
/^/ :规定字符以某个内容开始。
/$/:放在正则表达式的最后,叫做以什么内容结束。例如:/a$/:以a结束。
| : 在多个字符串任选其中某个字符串。
/a||b/ : a 或者 "" 或者 b。
开始案例:
//字符串必须由a开始
console.log(/^a/.test("abc"));//true
console.log(/^a/.test("cabc"));//false
结束案例:
console.log(/a$/.test("asdasda"));//true
console.log(/a$/.test("asdasd"));//false
| 的案例:
console.log(/ab|cd/.test("ab"));//true
/a||b/ 的案例:
console.log("acdb".match(/a||b/g));
console.log("acdb".match(/|a|b/g));//先查""
console.log("acdb".match(/a|b|/g));//先查a,再查b,最后查""
综合案例:
//指abc这个字符串包含了abc这个正则表达式
console.log(/abc/.test("abc"));//true
//abc必须等于abc
console.log(/^abc$/.test("abc"));//true
两个一起使用的案例:
8.群组
8.1 什么是群组
群组: 使用()括起来的就是群组。被()括起来的内容作为整体看。
8.2 群组取值
数组的第0项就是查找到所有满足正则内容。
第1项-第n项都是每个()群组的独立内容。
案例1:
console.log("13890790654".match(/^(\d{3})(\d{4})(\d{4})$/));
案例2:
console.log("a=4".match(/^(\w+)=(\d+)$/));
案例3:
console.log("ahjdgshaskjdhakjsdh".match(/^(\w{3})\w+(\w{4})$/));
案例4:
// 在使用match不能使用g来取多个群组内容, exec可以
console.log(/^(\d{3})(\d{4})(\d{4})$/g.exec("13890790654"));
案例5:
var str = "2[ab]3[cd]";
var str = "2[ab]";
console.log(str.match(/(\d+)\[(\w+)\]/g));
console.log(str.matchAll(/(\d+)\[(\w+)\]/g));
for (let value of str.matchAll(/(\d+)\[(\w+)\]/g)) {
console.log(value);
}
8.3 断言
断言:判断满足条件。
/a(?=b)/ :后置肯定断言 ,判断a后面紧跟一个b,获取这个a。
/a(?!b)/ :后置否定断言,判断a后面紧跟不是b,获取这个a。
/(?<=b)a/ :前置肯定断言,判断a前面紧跟是一个b 获取这个a。
/(?<!b)a/ :前置否定断言,判断a前面紧跟不是b 获取这个a。
案例:
console.log("acabad".replace(/a/g, "0"));//"0c0b0d"
console.log("acabad".replace(/a(?=b)/g, "0"));//"ac0bad"
console.log("acabad".replace(/a(?!b)/g, "0"));//"0cab0d"
console.log("cabada".replace(/(?<=b)a/g, "0"));//"cab0da"
console.log("cabada".replace(/(?<!b)a/g, "0"));//"c0bad0"
8.4 低级、中级、高级密码
1. 低级密码
/^\d{8,16}$|^[a-z]{8,16}$|^[A-Z]{8,16}$/
2. 中级密码
/^(?=\D+\d)(?=.*[a-z])[a-zA-Z0-9_.@!]{8,16}$|^(?=\D+\d)(?=.*[A-Z])[a-zA-Z0-9_.@!]{8,16}$|^(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9_.@!]{8,16}$/
3. 高级密码 :首字母不能是数字,但是整个密码中必须包含数字小写字母大写字母,可以使用!@._,8-16位。
/^(?=\D+\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9_.@!]{8,16}$/
(?=\D+\d) 条件起始字符必须是非数字1个以上,后面包含有数字 ”a1“ "asdasd1"。
(?=.*[a-z]) 条件起始字符 .*任意字符任意多个后面至少有一个小写字母a-z "ab" "Aa"。
(?=.*[A-Z]) 条件起始字符 .*任意字符任意多个后面至少有一个大写字母A-Z "ab" "Aa"。
这三个条件是并且关系。
[a-zA-Z0-9_.@!]{8,16} 这些字符总共8-16。
8.5 群组重复
(a)\1* :0个以上重复。
(a)\1+ : 1个以上重复。
案例:
//将 "aaaaabbbbbcccccccd"变成["aaaaa", "bbbbb", "ccccccc"]
let str = "aaaaabbbbbcccccccd";
console.log(str.match(/(\w)\1+/g));
console.log(str.match(/(\w)\1*/g));
8.6 群组变量
使用方法 :(?<变量名>n)
案例:
let str = "user=xietian&b=4";
console.log(str.match(/(?<name>\w+)=(?<value>\w+)/));
console.log(/(?<name>\w+)=(?<value>\w+)/.exec(str));
let arr = []
for (let value of str.matchAll(/(?<name>\w+)=(?<value>\w+)/g)) {
arr.push(value.groups);
}
console.log(arr);
案例:将 '2[3[ab]4[2[c]3[d]]]'变成"abababccdddccdddccdddccdddabababccdddccdddccdddccddd"
let str = '2[3[ab]4[2[c]3[d]]]'
function parse(str) {
if (!/\d+\[\w+\]/.test(str)) return str;
return parse(str.replace(/(\d+)\[(\w+)\]/, function (item, a, b) {
return b.repeat(a)
}))
}
str = parse(str);
console.log(str);
8.7 年月日的验证案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form action="">
<input type="text" id="year" />
<input type="text" id="month" />
<input type="text" id="day" />
</form>
<script>
var form = document.querySelector("form");
form.addEventListener("input", inputHandler);
function inputHandler(e) {
let input = e.target;
let bool = false;
if (/^year$/.test(input.id)) {
let date = new Date();
let year = date.getFullYear() % 2000;
let a = ~~(year / 10);
let b = year % 10;
bool = new RegExp(
`^19\\d{2}$|^20[0-${a - 1}]\\d$|^20${a}[0-${b}]$`
).test(input.value);
input.style.borderColor = bool ? "green" : "red";
input.bool = bool;
} else if (/^month$/.test(input.id)) {
bool = /^[1-9]$|^1[0-2]$/.test(input.value);
input.style.borderColor = bool ? "green" : "red";
input.bool = bool;
}
if (
Array.from(form.children)
.slice(0, 2)
.every((item) => item.bool)
) {
let year = form.children[0].value;
let month = form.children[1].value;
let date = new Date(Number(year), Number(month), 0);
let day = date.getDate();
let a = ~~(day / 10);
let b = day % 10;
let bool1 = new RegExp(
`^[1-9]$|^1\\d$|^2[0-${a > 2 ? "9" : b}]$` +
(a > 2 ? `|^3[0-${b}]$` : "")
).test(form.children[2].value);
form.children[2].style.borderColor = bool1 ? "green" : "red";
}
}
</script>
</body>
</html>