正则表达式相信是很多朋友的噩梦,它难读,它难写,看了一堆的字符,还是不知道如何下手。
说穿了,还是因为练习少了,认为它“难”,再加上网上很好找常用的正则表达式,所以压根就没有学习的欲望。
但其实,正则表达式也并没有那么“难”,这篇文章为大家准备了一些正则表达式示例,包括参考答案和分析思路,希望有需要的朋友能通过本文的练习有所收获。
示例练习
本文假设读者朋友至少已经了解正则表达式中各种字符的含义,以及常用方法等,如果不了解,那就去了解一下(或者打开文档) 😂。
匹配 QQ 号
QQ 号最少五位数,最多十位数。
分析:
- 不能全为 0;
- 第一位数最小值一定为 1,后面的任意位数取值范围为 0 - 9;
- 除去第一位数后,剩余位数的长度为 4 - 9 位。
因此,正则表达式可以为:
let regx = /^[1-9][\d]{4,9}$/;
let QQ1 = "9999";
let QQ2 = "01234";
let QQ3 = "12345";
let QQ4 = "1234567";
let QQ5 = "12345678";
let QQ6 = "123456789";
let QQ7 = "1234567890";
let QQ8 = "12345678900";
console.log(regx.test(QQ1)) //false
console.log(regx.test(QQ2)) //false
console.log(regx.test(QQ3)) //true
console.log(regx.test(QQ4)) //true
console.log(regx.test(QQ5)) //true
console.log(regx.test(QQ6)) //true
console.log(regx.test(QQ7)) //true
console.log(regx.test(QQ8)) //false
\d
等价于 [0-9]
,由于是第一个示例,所以提一下,后文就不再讲解字符含义了,朋友们可以查阅文档。
验证邮箱
邮箱的格式可以为 [email protected]
,或 [email protected]
等。
分析:
- 其实邮箱的格式是非常简单的,它只要求至少有一个
@
符号和一个.
符号,其余内容并没有要求。
因此,正则表达式可以为:
let regx = /^.+@.+\..+$/;
let emailA = "[email protected]";
let emailB = "[email protected]";
let emailC = "[email protected]";
let emailD = "[email protected]";
let emailE = "[email protected]";
let emailF = "123sdgf.sdggmail.com";
let emailG = "123sdgf.sdggmail";
let emailH = "123sdgf@sdggmail";
console.log(regx.test(emailA)); // true
console.log(regx.test(emailB)); // true
console.log(regx.test(emailC)); // true
console.log(regx.test(emailD)); // true
console.log(regx.test(emailE)); // true
console.log(regx.test(emailF)); // false
console.log(regx.test(emailG)); // false
console.log(regx.test(emailH)) // false
是不是非常简单呢?当然这是在完全不考虑特殊字符的情况下。
匹配月份
月份为两位数,最小为 01
,最大为 12
。
分析:
- 限定为两位数,多一位少一位都是错误的;
- 当第一位数为 0 时,第二位数的取值范围为 1 - 9;
- 当第一位数为 1 时,第二位数的取值范围为 0 - 2。
因此,正则表达式可以为:
let regx = /^0[1-9]|1[0-2]$/;
let month1 = "0";
let month2 = "00";
let month3 = "03";
let month4 = "10";
let month5 = "13";
let month6 = "010";
console.log(regx.test(month1)); //false
console.log(regx.test(month2)); //false
console.log(regx.test(month3)); //true
console.log(regx.test(month4)); //true
console.log(regx.test(month5)); //false
console.log(regx.test(month6)) //true
可以看到,month6
是三位数,也通过了验证。
这是因为,此例中出现了元字符 |
,它相当于逻辑“或”,即其左右的条件只要满足一个即可,而正则表达式在匹配过程中使用 0[1-9]
验证 010
时,前两位是满足的,因此停止了匹配,返回结果 true
。
处理的方法是用圆括号 ()
使其左右两侧的条件保持为一个整体,即:
let regx = /^(0[1-9]|1[0-2])$/;
let month6 = "010";
console.log(regx.test(month6)); //false
当匹配逻辑比较复杂时都可以使用 ()
来进行分组,一是方便阅读,二是避免类似上述情况发生,造成错误匹配。
匹配一个月的号数
号数为两位数,最小为 01
,最大为 31
。
分析:
- 限定为两位数,多一位少一位都是错误的;
- 当第一位数为 0 时,第二位数的取值范围为 1 - 9;
- 当第一位数为 1 或 2 时,第二位数的取值范围为 1 - 9;
- 当第一位数为 3 时,第二位数的取值范围为 0 或 1;
因此,正则表达式可以为:
let regx = /^(0[1-9]|[12]\d|3[01])$/;
let day1 = "0";
let day2 = "00";
let day3 = "02";
let day4 = "15";
let day5 = "20";
let day6 = "31";
let day7 = "32";
let day8 = "010";
console.log(regx.test(day1)); //false
console.log(regx.test(day2)); //false
console.log(regx.test(day3)); //true
console.log(regx.test(day4)); //true
console.log(regx.test(day5)); //true
console.log(regx.test(day6)); //false
console.log(regx.test(day7)); //false
console.log(regx.test(day8)); //false
匹配时间
时间的格式为 hh:mm:ss
。
分析:
- 小时最小为
00
,最大为24
,当第一位数为 0 或 1 时,第二位数的取值范围为 0 - 9;当第一位数为 2 时,第二位数的取值范围为 0 - 4; - 分钟最小为
00
,最大为59
,第一位数的取值范围为 0 - 5,第二位数的取值范围为 0 - 9;当小时为24
时,分钟只能为00
; - 秒数最小为
00
,最大为59
,第一位数的取值范围为 0 - 5,第二位数的取值范围为 0 - 9;当小时为24
时,分钟只能为00
;
因此,正则表达式可以为:
let regx = /^(([01]\d|2[0-3]):[0-5]\d:[0-5]\d)|24:00:00$/;
let timeA = "02:20:00";
let timeB = "24:00:00";
let timeC = "21:04:60";
let timeD = "00:00:00";
let timeE = "00:60:00";
let timeF = "21:60";
let timeG = "19-34:25";
console.log(regx.test(timeA)); // true
console.log(regx.test(timeB)); // false
console.log(regx.test(timeC)); // false
console.log(regx.test(timeD)); // false
console.log(regx.test(timeE)); // false
console.log(regx.test(timeF)); // false
console.log(regx.test(timeG)); // false
匹配日期
日期的格式为 yyyy-mm-dd
,最小年份为 0000
(仅作为公元 0 年的一种表现形式),最大年份定为 9999
。
分析:
- 年份的第一位数最小值一定是 1,当第一位数为 1 时,第二位一定是 9,其后两位数的取值范围为 0 - 9;当第一位数大于 1 时,其后三位数的取值范围为 0 - 9;
- 月份可以使用前文的分析;
- 号数此时会受到月份的影响,当月份为 2 时,号数最大值只能为 29,其余的与前文分析相同。
因此,正则表达式可以为:
let regx = /^(\d{4})-((0[13578])|(1[02])-([0-2][1-9])|(3[01]))|((0[469]|10)-([0-2][1-9]|30))|(02-[0-2][1-9])$/;
let dateA = "0000-02-30"; // 2月份没有30号
let dateB = "1987-02-29"; // 2月份有29号
let dateC = "1885-01-31"; // 1月份有31号
let dateD = "2020-01-30"; // 1月份有30号
let dateE = "1993-1-30"; // 月份必须是两位数
let dateF = "2020-10-3"; // 号数必须是两位数
let dateG = "1997-13-24"; // 月份必须小于等于12
let dateH = "2001-11-00"; // 号数必须大于等于01
let dateI = "2020-12-32"; // 号数必须小于等于31
let dateJ = "2020-10-30"; // 正确日期
let dateK = "2020-00-30"; // 月份必须大于等于01
let dateL = "999-05-30"; // 年份必须是四位数
console.log(regx.test(dateA)); // false
console.log(regx.test(dateB)); // true
console.log(regx.test(dateC)); // true
console.log(regx.test(dateD)); // true
console.log(regx.test(dateE)); // false
console.log(regx.test(dateF)); // false
console.log(regx.test(dateG)); // false
console.log(regx.test(dateH)); // false
console.log(regx.test(dateI)); // false
console.log(regx.test(dateJ)); // true
console.log(regx.test(dateK)); // false
console.log(regx.test(dateL)); // false
我相信还是有朋友看不懂这段正则表达式,因此特地准备了下面这张分解图:
是不是一下就清晰了。
匹配 IPV4 地址
IPV4 地址的格式为 192.168.1.25
,即由三个 .
分隔的四段数字,数字的取值范围为 0 - 255,除了长度为一位时外,0 不能出现在首位。
分析:
- 对于每段数字来说,当仅有一位数时,取值范围为 0 - 9;当有两位数时,取值范围为 10 - 99;当有三位数时,第一位取值为 1 时,第二三位取值范围都为 0 - 9,而第一位取值为 2 且第二位取值为 0 - 4 时,第三位取值范围为 0 - 9,当第一位取值为 2 且第三位取值为 5 时,第三位取值范围为 0 - 5;
- 每段数字的取值方式都是一样的;
- 前三段可以看作是
111.
重复了三遍。
因此,正则表达式可以为:
let regx = /^(((\d|([1-9]\d)|(1\d{2})|(2([0-4]\d|[0-5]{2})))\.){3}(\d|([1-9]\d)|(1\d{2})|(2([0-4]\d|[0-5]{2}))))$/;
let ipA = "0.1.1.1";
let ipB = "1.1.1.256";
let ipC = "1.01.5.255";
let ipD = "1.1.0.255.220";
let ipF = "1.199.0.210";
let ipG = "127.0.0.1";
console.log(regx.test(ipA)); // true
console.log(regx.test(ipB)); // false
console.log(regx.test(ipC)); // false
console.log(regx.test(ipD)); // false
console.log(regx.test(ipF)); // true
console.log(regx.test(ipG)); // true
看上去可能很复杂,但其实这是最简单直接的书写方式,它还有优化空间,留给朋友们自行研究。
为数字添加千分位分隔符
将输入的数字转为千分位表示形式,如 1234567
转为 1,234,567
,位数不足千位的不处理。
分析:
- 以三位数为一组进行分隔,在每一组的前一个位置上添加
,
,即,123
; - 在满足前一个条件的前提下,数字的开头不能有
,
,因为它已到达边界,如100000
,转换后应为100,000
。
因此,正则表达式可以为:
let regx = /\B(?=(\d{3})+$)/g;
let numberA = 1;
let numberB = 12;
let numberC = 123;
let numberD = 1234;
let numberE = 12345;
let numberF = 123456;
let numberG = 1234567;
let numberH = 12345678;
console.log(`${numberA}`.replace(regx, ',')); // 1
console.log(`${numberB}`.replace(regx, ',')); // 12
console.log(`${numberC}`.replace(regx, ',')); // 123
console.log(`${numberD}`.replace(regx, ',')); // 1,234
console.log(`${numberE}`.replace(regx, ',')); // 12,345
console.log(`${numberF}`.replace(regx, ',')); // 123,456
console.log(`${numberG}`.replace(regx, ',')); // 1,234,567
console.log(`${numberH}`.replace(regx, ',')); // 12,345,678
简单说一下这个示例,三位数字(必须是三位数字)为一组,而这样的组可以有多个,因此使用 (\d{3})+$
;由于我们是在每一组的前面添加 ,
,所以使用先行断言 (?=)
;而开头的 \B
就是匹配边界,以防止出现 ,123,456
的情况。
结语
本文示例后的参考答案不一定是最优解(当然也有可能有错误,欢迎指出),但作为练习是足够的,重在掌握分析思路,感兴趣的朋友可以继续深入。
另外,正则表达式中的各种字符记忆起来相对来说比较困难,能完全记住当然很好,不能记住也并不是什么大事,需要的时候查阅文档即可,更重要的是多加练习。