正则表达式

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

例如:

  • 将"JavaScript" 和 "java"两个字符串,能匹配到a字符吗或者能匹配到S字符吗?

  • 您能从"18912345678,my name is David." 这个字符串中能提炼出手机号码吗?

这些问题我们都能轻松的从正则表达式中找到解决方案。

构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与运算符可以将小的表达式结合在一起来创建更大的表达式。

正则表达式可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些的任意组合。

正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为"元字符")组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。

正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

正则表达式大部分语言都是支持的,例如:java,JavaScript,python,c 等,所以学好正则表达式,是非常有必要的,而且是通用。

语法

/正则表达式主体/修饰符(可选)

其中修饰符是可选的 (g i m)

实例:

var pattern= /www.xinbiancheng.cn/i

实例解析:

/xinbiancheng/i  是一个正则表达式。

xinbiancheng  是一个正则表达式主体 (用于检索)。

i  是一个修饰符 (搜索不区分大小写)。

通过前面的铺垫我们对正则表达式的语法格式有了一定的了解。

那我们的正则表达式就从最简单的一个字母开始吧:

示例

1、您能用正则表达式从JavaScript 和 Java 中分别挑选出他们中有几个a吗?

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>正则表达式 您能从JavaScript 和 Java 中分别挑选出他们中有几个a吗? xinbiancheng.cn</title>
</head>
<body>
  <script>
    var pattern = /a/g;  //定义的正则表达式
    var s = "JavaScript"; //字符串
    var a = s.match(pattern)
    console.log(a)   //从JavaScript字符串找到2个a,将他们放入数组中,打印内容为["a", "a"]
    s = "Java"; //字符串
    a = s.match(pattern)
    console.log(a)   //从Java字符串找到2个a,将他们放入数组中,打印内容为["a", "a"]
  </script>
</body>
</html>

上面完整代码是为了照顾没有经验的JavaScript同学,后面为了省字符只写部分代码段,原理同上。

前面的正则表达式的例子太简单了,有的同学在想,如果某一位上会不会有可能两种字符的情况呢?我们如何提炼呢?

正则表达式-描述字符范围

在正则表达式语法中,方括号表示字符范围。在方括号中可以包含多个字符,表示匹配其中任意一个字符。

如果多个字符的编码顺序是连续的,可以仅指定开头和结尾字符,省略中间字符,仅使用连字符‘-’表示。

如果在方括号内添加脱字符^前缀,还可以表示范围之外的字符。

例如:

  • [abc]:查找方括号内任意一个字符。
  • [^abc]:查找不在方括号内的字符。
  • [0-9]:查找从 0 至 9 范围内的数字,即查找数字。
  • [a-z]:查找从小写 a 到小写 z 范围内的字符,即查找小写字母。
  • [A-Z]:查找从大写 A 到大写 Z 范围内的字符,即查找大写字母。
  • [A-z]:查找从大写 A 到小写 z 范围内的字符,即所有大小写的字母。

示例

请在 JavaScript 和 Java 两个字符串中挑选出a和S两种字符

var pattern = /[aS]/g;  //正则表达式-描述字符范围 字符a、S两种字符
var s = "JavaScript";  //字符串直接量
var a = s.match(pattern)  //返回数组["a", "a", "S"]
s = "Java";
a = s.match(pattern) //返回数组["a", "a"]

示例

var s = "abcdez"; //字符串直接量
var pattern = /[abce-z]/g; //字符a、b、c,以及从e到z之间的任意字符
var a = s.match(pattern); //返回数组["a","b","c","e","z"]

示例

在中括号内不要有空格,否则会误解为还要匹配空格。

var r = /[0-9]/g;

示例

2021年的除夕是2月11日,请提炼除夕的月份

var str = "2021年的除夕是2月11日";
var m = /[0-9]月/;  //提取月份正则表达式
document.writeln(str.match(m))  //2月

示例

字符范围可以组合使用,以便设计更灵活的匹配模式。

var s = "abc4 abd6 abe3 abf1 abg7"; //字符串直接量
var r = /ab[c-g][1-3]/g; //前两个字符为ab,第三个字符为从c到g,第四个字符为1~7的任意数字
var a = s.match(r); //返回数组["abc4","abd6","abe3","abf1","abg7"]

示例

使用反义字符范围可以匹配很多无法直接描述的字符,达到以少应多的目的。

var s = "abc4 abd6 abe3 abf1 abg7"; //字符串直接量
var r = /ab[c-g][1-3]/g; //前两个字符为ab,第三个字符为从c到g,第四个字符为1-3的任意数字
var a = s.match(r); //返回数组["abe3", "abf1"]

示例

[^abc]:^代表去除的意思,意思是说只要不是a、b、c中的任意一个字符就可以。

下面用"xinbiancheng.cn"做实验,我们将用红色标出将要去除的字符

var s = "xinbiancheng.cn"; //字符串直接量
var r = /[^abc]/g; // 正则表达式
var a = s.match(r); //返回数组["x", "i", "n", "i", "n", "h", "e", "n", "g", ".", "n"]

正则表达式-描述字符

根据正则表达式语法规则,大部分字符仅能够描述自身,这些字符被称为普通字符,如所有的字母、数字等。

正则表达式 - 元字符

就是拥有特殊功能的特殊字符,大部分需要加反斜杠进行标识,以便于普通字符进行区别,而少数元字符,需要加反斜杠,以便转译为普通字符使用。

正则表达式支持的部分元字符如下表所示。

正则表达式部分元字符
元字符 描述
. 查找单个字符,除了换行和行结束符
\w 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]'
\W 查找非单词字符
\d 匹配一个数字字符。等价于 [0-9]
\D 查找非数字字符
\s 查找空白字符
\S 查找非空白字符
\b 匹配单词边界
\B 匹配非单词边界
\0 查找 NUL字符
\n 查找换行符
\f 查找换页符
\r 查找回车符
\t 查找制表符
\v 查找垂直制表符
\xxx 查找以八进制数 xxxx 规定的字符
\xdd 查找以十六进制数 dd 规定的字符
\uxxxx 查找以十六进制 xxxx规定的 Unicode 字符
x|y 匹配 x 或 y 例如,'a|b' 能匹配 "a" 或 "b"。'(a|b)ccc' 则匹配 "accc" 或 "bccc"

更详细正则表达式元字符请点击

正则表达式-选择匹配

选择匹配类似于 JavaScript 的逻辑与运算,使用竖线|描述,表示在两个子模式的匹配结果中任选一个。例如:
1) 匹配任意字母或数字

var r = /\w|\d/;  //定义正则表达式的选择匹配模式
//  \w 代表的是配置字母
//  \d 代表的是匹配数字
//用正则表达式的选择匹配模式合并起来的意思是:匹配任意字母或数字

示例

我们在做网页开发的时候,需要替换标签中的字符,例如:`<a href='/4.mp3' singer="beyond">光辉岁月</a>`我们想去掉a标签中的'单引号或"双引号

如何解决呢?首先设计一个将要去掉的列表,然后使用竖线把它们连接在一起,定义正则表达式的选择匹配模式,最后使用字符串的 replace() 方法把所有敏感字符替换为空字符串。代码如下:

var s = `<a href='/4.mp3' singer="beyond">光辉岁月</a>`
var r = /\'/g  //正则匹配'单引号
var s = s.replace(r, ''); //用正则表达式替换'单引号
console.log(s)   //<a href=/4.mp3 singer="beyond">光辉岁月</a>

r = /\"/g      //正则匹配"双引号
s = s.replace(r, ''); //用正则表达式替换"双引号
console.log(s)  //<a href=/4.mp3 singer=beyond>光辉岁月</a>

console.log('------------') //分隔线

var s1 = `<a href='/4.mp3' singer="beyond">光辉岁月</a>`
var r1 = /\'|\"/g  //中间加|使用的是正则表达式的选择匹配,'或"两种字符都可以
s1 = s1.replace(r1, ''); //用选择匹配的正则表达式可以同时替换单双引号两种字符
console.log(s1) //<a href=/4.mp3 singer=beyond>光辉岁月</a>

打印结果如下:

<a href=/4.mp3 singer="beyond">光辉岁月</a>
<a href=/4.mp3 singer=beyond>光辉岁月</a>
------------
<a href=/4.mp3 singer=beyond>光辉岁月</a>

正则表达式-重复匹配

在正则表达式语法中,定义了一组重复类量词,如表所示。它们定义了重复匹配字符的数量。
 

重复类量词列表
量词 描述
n+ 匹配任何包含至少一个 n 的字符串
n* 匹配任何包含零个或多个 n 的字符串
n? 匹配任何包含零个或一个 n 的字符串
n{x} 匹配包含 x 个 n 的序列的字符串
n{x,y} 匹配包含最少 x 个、最多 y 个 n 的序列的字符串
n{x,} 匹配包含至少 x 个 n 的序列的字符串

示例

首先定义一个字符串:

var s = "bancheng biancheng biiancheng biiiancheng biiiiancheng biiiiiancheng";

1) 如果仅匹配单词 bancheng和 biancheng,可以设计:

var r = /bi?ancheng/g;  //i?匹配任何包含零个或一个i的字符串,如果是0个i,就是bancheng,如果是1个i就是biancheng
var a = s.match(r);

量词?表示前面字符或子表达式为可有可无,等效于:

var r = /bi{0,1}ancheng/g; 
var a = s.match(r);

2) 如果想匹配biiiancheng,中间有3个i,正则表达式定义为:

var r = /bi{3}ancheng/g;
var a = s.match(r);

等同于:

var r = /biiiancheng/g;
var a = s.match(r);

3) 如果期望匹配biiancheng biiiancheng biiiiancheng这几个单词,正则表达式如下:

var r = /bi{2,4}ancheng/g;
var a = s.match(r);

4) 如果匹配所有单词,正则表达式如下:

var r = /bi*ancheng/g;
var a = s.match(r);

量词*表示前面字符或表达式可以不出现,或者重复出现任意多次。等价于:

var r = /bi{0,}ancheng/g;
var a = s.match(r);

5) 如果期望匹配包含字符“i”的所有单词词,正则表达式如下:

var r = /bi+ancheng/g;
var a = s.match(r);

量词+表示前面字符或子表达式至少出现 1 次,最多重复次数不限。等价于:

var r = /bi{1,}ancheng/g;
var a = s.match(r);

重复类量词总是出现在它们所作用的字符或子表达式后面。如果想作用于多个字符,需要使用小括号把它们包裹在一起形成一个子表达式。

正则表达式-惰性匹配

重复类量词都具有贪婪性,在条件允许的前提下,会匹配尽可能多的字符。

  • ?、{n} 和 {n,m} 重复类具有弱贪婪性,表现为贪婪的有限性。
  • *、+ 和 {n,} 重复类具有强贪婪性,表现为贪婪的无限性。

正则表达式-重复类量词匹配左边优先级比右边的优先级高。当多个重复类量词同时满足条件时,会在保证右侧重复类量词最低匹配次数基础上,使最左侧的重复类量词尽可能多占有字符

示例

正则表达式-贪婪匹配

var s = '<head><meta charset="utf-8"><meta name="viewport"/><title>xinbiancheng.cn</title></head>';
var r = /(<.*>)(<.*>)/
/*
正则表达式:解释说明
()代表子表达式,有2组(),说明如果匹配成功会有2组。
接着再看括号里面的字符,括号里面的第一个字符<左尖括号,正好跟标签的<左尖括号一样,
第二个是.小数点符号,代表匹配除换行符(\n、\r)之外的任何单个字符
第二个是*星号,代表匹配任何包含零个或多个的字符串
.*的组合表示的是除换行符(\n、\r)之外的任何单个字符任意组合成长度不固定的字符串
最后一个字符是>右尖括号
所以最后表达的意思是<>左右尖括号中间包含的是不定长的字符串,那它就是html标签的特征
*/
var a = s.match(r);
console.log(a[1]);  //2组(),左边()*代表贪婪式的尽量多,但必须留一个匹配的给右边()
//所以a[1]' = <head><meta charset="utf-8"><meta name="viewport"/><title>xinbiancheng.cn</title>'
console.log(a[2]);  //右侧()表达式匹配,留一个匹配的,那就只剩下 '</head>'
//a[2] = '</head>'
与贪婪匹配相反,惰性匹配将遵循另一种算法:在满足条件的前提下,尽可能少的匹配字符。定义惰性匹配的方法:在重复类量词后面添加问号?限制词。贪婪匹配体现了最大化匹配原则,非贪婪匹配则体现最小化匹配原则。

示例

正则表达式-非贪婪匹配

var s = "<head><meta charset="utf-8"><meta name="viewport"/><title>xinbiancheng.cn</title></head>";
var r = /<.*?>/
var a = s.match(r); //返回单个元素数组["<head>"]

以上代码的正则表达式:解释说明,上一个代码的<.*>正则表达式已经解释清楚了,现在在*号后面又多了一个?号,?号的含义是,匹配任何包含零个或一个的字符串,有了这个条件的限制,所以最多只能匹配到一个,故只能匹配到一个标签["<head>"]。

针对 6 种重复类非贪婪匹配的简单描述如下:

  • {n,m}?:尽量匹配 n 次,但是为了满足限定条件也可能最多重复 m 次。
  • {n}?:尽量匹配 n 次。
  • {n,}?:尽量匹配 n 次,但是为了满足限定条件也可能匹配任意次。
  • ??:尽量匹配,但是为了满足限定条件也可能最多匹配 1 次,相当于 {0,1}?。
  • +?:尽量匹配 1 次,但是为了满足限定条件也可能匹配任意次,相当于 {1,}?。
  • *? :尽量不匹配,但是为了满足限定条件也可能匹配任意次,相当于 {0,}?。

正则表达式-边界量词

边界就是确定匹配模式的位置,比如字符串的头部或尾部。

正则表达式支持的边界量词
量词 说明
^ 匹配开头,在多行检测中,会匹配一行的开头
$ 匹配结尾,在多行检测中,会匹配一行的结尾


下面代码演示如何使用边界量词。先定义字符串:

var s = 'xinbiancheng baidu google';

1) 正则表达式-匹配每一个单词

var r = /\w+/g;
var a = s.match(r); //返回数组["xinbiancheng","baidu","google"]

解释说明:\w代表匹配字母、数字、下划线。等价于'[A-Za-z0-9_]',后面又紧跟着一个+号,代表最少匹配一个前面的描述,然而空格不在[A-Za-z0-9_]这个描述的范围,所以在空格的地方会作为分割,正则表达式的最后出现了个一g的字符,代表的是正则表达式的修饰符,意思是从整个字符串中,查找出全部匹配的数据,所以结果会出现3个单词,["xinbiancheng","baidu","google"]

2) 正则表达式-匹配第一个单词

var r = /^\w+/g;
var a = s.match(r); //返回数组["xinbiancheng"]

^代表的是,匹配开头,在多行检测中,会匹配一行的开头,前面的正则表达式已经分析的比较清楚了,只是加了一个限制条件^,匹配开始的部分,也就是["xinbiancheng"]

3) 正则表达式-匹配最后一个单词

var r = /\w+$/g;
var a = s.match(r); //返回数组["google"]

$代表的是,匹配结尾,在多行检测中,会匹配一行的结尾,结果为["google"]

正则表达式-环视(Lookaround)

环视只进行子表达式的匹配,不占有字符,匹配到的内容不保存到最终的匹配结果,是零宽度的。环视匹配的最终结果就是一个位置。

环视的作用相当于对所在位置加了一个附加条件,只有满足这个条件,环视子表达式才能匹配成功。

环视按照方向划分有顺序和逆序两种,按照是否匹配有肯定和否定两种,组合起来就有四种环视。顺序环视相当于在当前位置右侧附加一个条件,而逆序环视相当于在当前位置左侧附加一个条件。

表达式

说明

(?<=Expression)

逆序肯定环视,表示所在位置左侧能够匹配Expression

(?<!Expression)

逆序否定环视,表示所在位置左侧不能匹配Expression

(?=Expression)

顺序肯定环视,表示所在位置右侧能够匹配Expression

(?!Expression)

顺序否定环视,表示所在位置右侧不能匹配Expression

对于环视的叫法,有的文档里叫预搜索,有的叫什么什么断言的,有的叫“环视”,其实叫什么无所谓,只要知道是什么作用就是了可以了

先记住顺口溜 顺右逆左,正合我意

正则表达式-环视顺序肯定

案例:

如果我们想检测字符串中是否含有 Windows98,Windows2000,WindowsXP 等相关的版本信息,如果有就提炼出Windows这一个字符串

var s = "This system is Windows98";
var r = /Windows(?=98|2000|XP)/; 
var a = s.match(r);  //返回数组["Windows"]

正则表达式-顺序环视 分析 /Windows(?=98|2000|XP)/ 前面的正则分为两部分 Windows 和 (?=98|2000|XP),第一部分Windows 匹配 "This system is Windows98"是ok的,字符串走到"9这个位置,

控制权就交接给第二部分(?=98|2000|XP),第二部分有三个条件98|2000|XP其中有一个跟98匹配。环视有一个特点只记位置信息,而不存储信息:所以返回的是Windows而不是Windows98

正则表达式-环视顺序否定

案例:

请挑选出 不是 Windows98,Windows2000,WindowsXP 等相关的版本信息,如果有就提炼出Windows这一个字符串

var s = "This system is Windows32";
var r = /Windows(?!98|2000|XP)/; 
var a = s.match(r);  //返回数组["Windows"]

第一部分分析跟上一个案例是一样的。当控制权交接给第二部分(?!98|2000|XP)发现32 不满足98|2000|XP这几个条件,就不成立,但是这3个条件的前面加了?! 这个代表的是否定的意思,这样反而就匹配成功了。结果为返回数组["Windows"]

正则表达式-捕获组

捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用。当然,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部。

捕获组有两种形式,一种是普通捕获组,另一种是命名捕获组,通常所说的捕获组指的是普通捕获组。

大部分语言都支持捕获组,用()来表示例如:2021-2-11,可以分为3个组(\d{4})-(\d{1,2})-(\d\d) 用3对括号来表示,编号分别是1,2,3

项目案例:

请提炼出日期中的年月日

var s = '2021-2-11'
var r = /(\d{4})-(\d{1,2})-(\d\d)/  //正则表达式捕获组
var a = s.match(r);
console.log(a[1]) //2021  对应边编号1
console.log(a[2]) //2     对应边编号2
console.log(a[3]) //11    对应边编号3

正则表达式-反向引用

在正则表达式对象的 test() 方法中,以及字符串对象的 match() 和 search() 等方法中使用。在这些方法中,反向引用的值可以从 RegExp() 构造函数中获得。

var s = "xinbiancheng.cn";
var r = /(\w)(\w)(\w)/;
r.test(s);
console.log(RegExp.$1);  //返回第1个子表达式匹配的字符x
console.log(RegExp.$2);  //返回第2个子表达式匹配的字符i
console.log(RegExp.$3);  //返回第3个子表达式匹配的字符n

正则表达式执行匹配检测后,所有子表达式匹配的文本都被分组存储在 RegExp() 构造函数的属性内,通过前缀符号$与正则表达式中子表达式的编号来引用这些临时属性。其中属性 $1 标识符指向第 1 个值引用,属性 $2 标识符指向第 2 个值引用,以此类推。

实例

可以直接在定义的字符模式中包含反向引用。这可以通过使用特殊转义序列(如 \1、\2、\3 等)

var s = "abcbcacba";
var r = /(\w)(\w)(\w)\2\3\1\3\2\1/;
var b = r.test(s); //验证正则表达式是否匹配该字符串
console.log(b); //返回true

在上面实例的正则表达式中,“\1”表示对第 1 个反向引用 (\w) 所匹配的字符 a 进行引用,“\2”表示对第 2 个反向引用 (\w) 所匹配的字符串 b 进行引用,“\3”表示对第 3 个反向引用 (\w) 所匹配的字符 c 进行引用。

实例

正则表达式在字符串对象的 replace() 方法中,通过使用特殊字符序列$1、$2、$3 替换非常实用。例如:如何将相邻字母和数字颠倒位置呢?

var s = "a1b2c3d4e5f6";
var r = /(\w+?)(\d+)/g;
var b = s.replace(r, "$2$1");
console.log(b); //1a2b3c4d5e6f

案例分析:正则表达式包括两个分组,第 1 个分组匹配任意连续的字母,第 2 个分组匹配任意连续的数字。例如:a1满足2个分组所以$1=a $2=1

经过正则表达的s.replace(r, "$2$1"); $2$1交互了位置就变成了1a,以此类推,就把字符串"a1b2c3d4e5f6" 变成了 "1a2b3c4d5e6f"

由于篇幅有限,更多正则表达式,请点击相关的章节