Skip to content

正则表达式

概述

正则表达式(RegExp)是匹配、检索、替换字符串规则的语法模板,用来快速校验格式、提取内容、批量替换文本。

1
2
3
4
5
6
7
8
9
// 手机号
const phone = /^1\d{10}$/;
phone.test("13800138000"); // true

// 邮箱简易规则
const email = /^\w+@\w+\.\w+$/;

// 替换空格
"a b c".replace(/\s/g,"");

为什么需要正则表达式

以邮政编码校验为例,对比传统方法和正则表达式检验的区别。

邮政编码规则

  1. 总长度固定:必须严格 6 位字符,不能多、不能少;
  2. 字符类型:只能是纯阿拉伯数字 0–9,不允许字母、汉字、空格、符号;
  3. 首尾规范:第一位不能为 0(常规通用校验规则),后5位可为任意数字;
  4. 无空值:不允许空白、不全、补零占位;
  5. 无拼接:不能有加号、横杠、括号等分隔符,纯6位连续数字。
验证邮政编码,6位数字
<script>
  //使用传统方法
  function check1(s) {
    if (s.length != 6) return false;
    var numbers = "0123456789";
    for (var i = 0; i < 6; i++) {
      var ch = s.substr(i, 1);
      if (numbers.indexOf(ch) == -1) return false;
    }
    return true;
  }

  //使用正则表达式
  function check2(s) {
    return /^\d{6}$/.test(s);
  }
</script>
源代码
front/js/advanced/regular/src/js_regular_post_code.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>rect</title>
</head>

<body>
  <script>
    var code = "123456"
    alert(check1(code));
    alert(check2(code));

    //使用传统方法
    function check1(s) {
      if (s.length != 6) return false;
      var numbers = "0123456789";
      for (var i = 0; i < 6; i++) {
        var ch = s.substr(i, 1);
        if (numbers.indexOf(ch) == -1) return false;
      }
      return true;
    }

    //使用正则表达式
    function check2(s) {
      return /^\d{6}$/.test(s);
    }
  </script>
</body>

</html>

正则表达式语法

正则由普通字符 + 元字符 + 限定符 + 修饰符组成,用来匹配字符串规则。

  • 普通字符:仅作字面匹配、无特殊语义的基础字符。
  • 元字符:拥有特殊含义的字符,用来描述字符串模式。
  • 限定符:用来限制元字符的匹配次数。
  • 修饰符:用来改变正则表达式的匹配行为。
  • a|b 匹配表达式a或者b,例如/Hello (World|Kitty)!/ 可以匹配"Hello World!"或"Hello Kitty!"

  • [abc] 匹配方括号中的任意字符,例如/[drz]oom/可以匹配"doom"、"room"或"zoom"

  • [^abc] 匹配不在方括号中的任意字符

  • [0-9] 匹配任意从0到9的数字,例如 /No[3-6]/ 可以匹配"No3"、"No4"、"No5"、"No6"

  • [a-z] 匹配任意从a到z的小写字母,[A-Z] 匹配任意从A到Z的大写字母。数字、小写、大写字母三种范围可以组合使用,例如 /[7-8c-eF-H]/ 可以匹配字符7、8、c、d、e、F、G、H; /[^0-9a-zA-Z]/ 可以匹配除了数字和字母以外的任意字符。

元字符,拥有特殊含义的字符:

  • . 匹配除了换行符(\n)以外的任何单个字符

  • \d 匹配一个数字字符,等价于 [0-9]

  • \D 匹配一个非数字字符,等价于 [^0-9]

  • \t 匹配一个制表符

  • \n 匹配一个换行符

  • \r 匹配一个回车符

  • 注意:Windows下每行结尾使用\r\n,Unix/Linux下只有\n

  • \s 匹配任何空白字符(空格、换行、回车、制表符……)

  • \S 匹配任何非空白字符

  • \w 匹配单词字符,等价于 [A-Za-z0-9_]

  • \W 匹配非单词字符,等价于 [^A-Za-z0-9_]

*\unnnn 匹配一个unicode字符,nnnn为16进制unicode编码,例如 /[\u4e00-\u9fa5]/ 匹配所有中文字符

  • \xnn 匹配一个ASCII字符,例如 /[\x00-\xff]/ 匹配所有ASCII字符

匹配字符位置:

  • ^ 匹配字符串开头位置,例如 /^h/ 可以匹配"hello"中的"h",但不能匹配"something"中的"h"

  • $ 匹配字符串结束位置,例如 /h$/ 可以匹配"length"中的"h",但不能匹配"something"中的"h"

  • \b 匹配单词边界,例如 /or\b/ 可以匹配"for you"或者"yes or no"中的"or",但不能匹配"orange"中的"or"

  • \B 匹配非单词边界

  • (?=s) 正向预查,匹配表达式s前面的位置,例如 /Windows (?=XP|98)/ 可以匹配"Windows XP"或"Windows 98"中的"Windows",但不能匹配"Windows 7"中的"Windows"

  • (?!s) 负向预查,匹配不在表达式s前面的位置,例如 /Windows (?!XP)/ 可以匹配除了在"XP"前面之外的"Windows"字符串

量词,匹配出现次数:

  • {n} 匹配前面的表达式n次,例如 /[A-Z]{3}/ 匹配任意三个大写字母,如"CPU"、"IBM",但不能匹配"W3C"

  • {n,m} 匹配前面的表达式最少n次,最多m次(n<=m),例如 /\d{6,8}/ 可以匹配"123456","7654321","88888888"

  • {n,} 匹配前面的表达式最少n次,例如 /go{2,}gle/ 可以匹配"google"、"gooogle"、"goooooooooooogle"

  • * 匹配前面的表达式零次或多次,例如 /#\d*/ 可以匹配"#"、"#1"、"#256"、"#28875474932"

  • + 匹配前面的表达式一次或多次,例如 /ah+/ 可以匹配"ah"、"ahh"、"ahhhhhhhhh"

  • ? 匹配前面的表达式零次或一次,例如 /apples?/ 可以匹配"apple"或"apples"

转义字符:\

需要转义的字符:\ / ^ $ * + ? { } [ ] ( ) . |

例如:

  • /^\$\d+\.\d{2}$/ 可以匹配"$123.45"、"$0.25"等

  • /^\*\.(html|js|css)$/ 可以匹配".html"、".js"、"*.css"

  • /^\(\d{3,4}\)\d{8}$/ 可以匹配"(0431)88951212"、"(010)82619957"等

  • /^[_\$A-Za-z][_\$A-Za-z0-9]*$/ 可以匹配"hello"、"$123"、"_user"、"num10"


示例

编码 规则 示例 正则表达式
手机号码 1. 一共11位数字;
2. 第一位必须是1;
3. 第第二位可以是3、5、8
"13712345678" ^1[358]\d{9}$
QQ号码 大于或等于10000的数字 "10001" ^[1-9]\d{4,}$
浮点数 1. 前面可以带负号;
2. 整数至少1位;
3. 小数任意位可以没有小数位
"3.14159" ^-?\d+(\.\d+)?$
带区号和分机号的电话号码 1. 城市区号必须以0开头,共3位或4位;
2. 电话号8位;
3. 分机号3位,分机号部分可有可无
"(0431)88951212-152" ^\(0\d{2,3}\)\d{8}(-\d{3})?$
国际电话号码 1. 由空格分成4个部分,
2. 第一部分+号后面接1~4位国际区号;
3. 第二部分后面接1~4位国际区号,第二部分3-4位地区号不能以0开始,第三部分3-4位,第四部分4位
"+86 431 8895 1212" ^\+\d{1,4} \d{3,4} \d{3,4} \d{4}$
IP地址 1. 由小数点分开的4段;
2. 每段都是0~255之间的数字
"10.0.2.15" ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
电子邮件地址 1. @前面至少1个字符(可以是数字、字母、下划线、加减号、小数点,加减号和小数点不能在两端);
2. @后面由.分隔成至少2段;
3. 每段至少1个字符(可以是数字、字母、下划线、减号、小数点,减号和小数点不能在两端)
"jack@java-fans.com" ^[a-zA-Z0-9_]([a-zA-Z0-9._+-]*[a-zA-Z0-9_])?@[a-zA-Z0-9_]([a-zA-Z0-9.-]*[a-zA-Z0-9_])?\.[a-zA-Z]{2,}$

使用正则表达式进行替换

字符串替换:

var s = "hello world!";
alert(s.replace("o", "x")); //"hellx wxrld!"

正则表达式替换:

1
2
3
4
5
6
var s = "8 apple is $10, 50 banana is $200";
alert(s.replace(/\$\d+/, "free"));
alert(s.replace(/\$\d+/g, "free"));
//g表示全局匹配,i表示忽略大小写,m执行多行匹配
alert(s.replace(/\$(\d+)/g, "¥$1"));
//用小括号获取匹配,用$1...$n使用获取的字符串

使用正则表达式获取匹配

另一个获取匹配的例子:

var s = "姓名:张三,性别:男,年龄:20";
alert(s.replace(/^姓名:(.+),性别:(.+),年龄:(.+)$/, "有一个$3岁的$2人叫$1"));

在匹配时使用获取匹配:

1
2
3
4
5
6
7
8
var s = "The food in Hollywood is good.";
//要求:将连续两个相同的字母替换成"xx"
alert(s.replace(/o{2}/g, "xx"));
//结果:"The fxxd in Hollywxxd is gxxd."
alert(s.replace(/[a-z]{2}/g, "xx"));
//结果:"Txx xxxx xx Hxxxxxxxx xx xxxx."
alert(s.replace(/([a-z])\1/g, "xx"));
//结果:"The fxxd in Hoxxywxxd is gxxd."

练习

1
2
3
4
var s = "小鸡唱歌叽叽叽,小鸭唱歌嘎嘎嘎,小朋友大笑hahaha";
//要求:把重复三次的部分替换成重复两次,并在每次的后面加上波浪线
//结果:"小鸡唱歌叽~叽~,小鸭唱歌嘎~嘎~,小朋友大笑ha~ha~"
alert(s.replace(/(.+)\1\1/g, "$1~$1~"));
1
2
3
4
5
var s = "<h1>静夜思</h1><p>床前明月光</p><p>疑是地上霜</p><p>举头望明月</p><p>低头思故乡</p>";

//要求:去掉HTML标签(可以是任意标签)
//结果:"静夜思床前明月光疑是地上霜举头望明月低头思故乡"
alert(s.replace(/<\/?[a-z1-6]+>/g, ""));
1
2
3
//要求:去掉HTML标签并在每句后面加斜线“/”
//结果:"静夜思/床前明月光/疑是地上霜/举头望明月/低头思故乡/"
alert(s.replace(/<([a-z1-6]+)>(.*?)<\/\1>/g, "$2/"));

跟在量词后面的问号代表按非贪婪模式匹配(缺省都是贪婪模式,即尽可能多的匹配字符)