正则表达式相信是很多朋友的噩梦,它难读,它难写,看了一堆的字符,还是不知道如何下手。

说穿了,还是因为练习少了,认为它“难”,再加上网上很好找常用的正则表达式,所以压根就没有学习的欲望。

但其实,正则表达式也并没有那么“难”,这篇文章为大家准备了一些正则表达式示例,包括参考答案和分析思路,希望有需要的朋友能通过本文的练习有所收获。

示例练习

本文假设读者朋友至少已经了解正则表达式中各种字符的含义,以及常用方法等,如果不了解,那就去了解一下(或者打开文档) 😂。

匹配 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],由于是第一个示例,所以提一下,后文就不再讲解字符含义了,朋友们可以查阅文档。

验证邮箱

邮箱的格式可以为 qingcong@163.com,或 qingcong@163.com.cn等。

分析:

  • 其实邮箱的格式是非常简单的,它只要求至少有一个 @ 符号和一个 . 符号,其余内容并没有要求。

因此,正则表达式可以为:

let regx = /^.+@.+\..+$/;

let emailA = "123@qq.com";
let emailB = "123@gmail.com";
let emailC = "123sdgfsdg@gmail.com";
let emailD = "123sdgf.sdg@gmail.com";
let emailE = "123sdgf.sdg@gmail.com.cn";
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

我相信还是有朋友看不懂这段正则表达式,因此特地准备了下面这张分解图:

explanation

是不是一下就清晰了。

匹配 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 的情况。

结语

本文示例后的参考答案不一定是最优解(当然也有可能有错误,欢迎指出),但作为练习是足够的,重在掌握分析思路,感兴趣的朋友可以继续深入。

另外,正则表达式中的各种字符记忆起来相对来说比较困难,能完全记住当然很好,不能记住也并不是什么大事,需要的时候查阅文档即可,更重要的是多加练习。

最近更新:
作者: MeFelixWang