对于正则表达式可是又爱又恨,爱的是正则表达式在编程中实用作用大,恨的是学了太容易忘记,因此将正则表达式的语法记录下来,加深记忆,又方便查阅。

正则表达式(Regular Expression,简写:RegExp)可以用来匹配或替换某些模式的字符,而正则表达式则是这些模式。

当写的正则表达式不确定是否正确时,可以使用测试工具测试:在线表达式测试。当不太确定正则表达式的意思时,可以使用分析工具分析:正则分析器


创建RegExp

创建正则表达式的方法有两种:

1. 直接量语法

直接通过斜杠/包住元字符即可声明一个正则表达式:

var reg = /hello/

2. 创建RegExp对象的语法

通过RegExp实例化:

var reg = new RegExp('hello' , 'g')

RegExp第一个参数为正则表达式,第二个参数可选,修饰符。下面将会有详解介绍。


语法

正则表达式由一些普通的字符和元字符组成。普通的字符比如匹配hello这个字符,那么正则表达式可以写为/hello/。而元字符有多种,可以根据元字符的用法分为几类:基本元字符、定义类元字符、数量类元字符、位置类元字符、修饰符类字符。

基本元字符:

基本元字符划分较基本的元字符:

  1. . : 匹配除换行符\n之外的任何字符。如/./,可以匹配aA_等非换行符字符。

  2. \ : 与下一个字符搭配,匹配为一个特殊字符。如/n/,匹配的是一个n字符,如果正则表达式为/\n/,则匹配一个换行符。

  3. | : 逻辑或操作符。

var reg = /hello|world/g,
str = 'world';

console.log(str.match(reg)) // ["world"]
  1. [ ] : 匹配字符的合集,可匹配集合中的任何一个字符。在[ ].*\等表示其本身。
var reg = /1[234]2/g,
str = '12 122 112 1321';

console.log(str.match(reg)) // ["122", "132"]
  1. [^] : 表示非,对集合里面的字符取非。如[^.*],可以匹配不为.*的字符。

  2. - : 在[]中使用,定义一个区间。如/[a-z]/,可以匹配a到z的小写字母。

  3. () : 表示分组。

//可以用来匹配hello xxx也可以用来匹配helloworld
var reg = /(hello|helloworld) xxx/;
console.log(reg.test('hello xxx')) //true
console.log(reg.test('helloworld xxx')) //true

()还可以用来表示捕获括号,捕获括号里的表达式叫做子表达式,捕获的子串可以通过RegExp的属性$1、$2...$9获得。

var reg = /(\d+)-(\d+)-(\d+)/,
str = '1980-9-8';

reg.test(str);

console.log(RegExp.$1); //"1980"
console.log(RegExp.$2); //"9"
console.log(RegExp.$3); //"8"

如果不想捕获,只想使用分组的功能,可以使用(?:p)表示不捕获。

var reg = /(\d+)-(?:\d+)-\d+/,
str = '1980-9-7';

reg.test(str);

console.log(RegExp.$1); //"1980"
console.log(RegExp.$2); //""


定义类元字符:

定义类元字符表示用某些字符表示一类字符:

  1. \d : 匹配一个数字字符,等价于/[0-9]/。digit的缩写。

  2. \D : 匹配一个非数字的字符,等价于/[^0-9]/

  3. \w : 匹配一个大小写字母数字下划线的字符,等价于/[a-zA-Z0-9_]/。word的缩写。

  4. \W : 匹配一个非大小写字母数字下划线的字符,等价于/[^a-zA-Z0-9_]/

  5. \s : 匹配一个空白符,包括空格、换行符(\n)、回车符(\r)、水平制表符(\t)、垂直制表符(\v)、换页符(\f),等价于/[\n\r\t\v\f]/。space的缩写。

  6. \S : 表示一个非空白符。等价于/[^\n\r\t\v\f]/


数量类元字符:

数量类元字符表示前面的字符匹配的数量:

  1. {m} : 表示前面的字符重复m次。如/\d{2}/,匹配两个数字,如可以匹配12。

  2. {m,n} : 表示前面的字符至少重复m次,至多重复n次。如/\d{2,4}/,123和32都可以匹配得到。另外,{m,}表示至少m次。

  3. ? : 表示前面的字符出现0次或1次。等价于{0,1}。还有一种情况,默认情况下,正则的匹配是贪婪模式,?如果跟在量词*{}?+后面,就会使得匹配变成非贪婪模式。下面将会介绍贪婪模式和非贪婪模式。

//贪婪模式: 尽可能的匹配更多的字符
var reg = /\w{2,4}/;
str = 'abcde';
console.log(str.match(reg)[0]); //abcd

//贪婪模式:按照至少的字符量来匹配
var reg2 = /\w{2,4}?/;
str = 'abcde';
console.log(str.match(reg2)[0]); //ab
  1. + : 表示前面的字符至少出现一次。等价于{1,}。

  2. * : 表示前面的字符出现零次或多次。等价于{0,}。


位置类元字符:

位置类元字符匹配的是位置,而非前面所说的字符。如字符串abc,这个字符共有4个位置:(位置)a(位置)b(位置)c(位置)。

  1. ^ : 匹配的是表达式的开始位置。

  2. $ : 匹配的是表达式的结束位置。

//在abc的开始位置和结束位置插入`*`
var reg = /^|$/g,
str = 'abc';
console.log(str.replace(reg , '*')) //*abc*
  1. (?=p) : 匹配的是p前面的位置。如在字符aa前插入bb
var str = 'baac'.replace(/(?=aa)/,'bb');
console.log(str) //bbbaac
  1. (?!0) : 匹配的不是p前面的位置。如在非字符aa前插入dd
var str = 'baac'.replace(/(?!aa)/,'dd');
console.log(str) //ddbaac
  1. \b : 匹配单词的边界。单词边界指的是\w([a-zA-Z0-9_])\W之前的边界,包括\w^以及$之间的位置。
//在单词边界插入*
var reg = /\b/g,
str = 's$kdh ssdf[ac] s_c#';
str.replace(reg , '*') //*s*$*kdh* *ssdf*[*ac*] *s_c*#
  1. \B : 匹配非单词的边界。


修饰符类字符:

  1. g : 全局匹配
//用*代替ab,reg1为全局搜索,reg2未设全局搜索
var reg1 = /ab/g, //全局搜索,全部匹配的结果都替换掉
reg2 = /ab/; //搜索成功一次就替换,不搜索下去

var str = 'abc abd';

console.log(str.replace(reg1 , '*')) //"*c *d"
console.log(str.replace(reg2 , '*')) //"*c abd"
  1. i : 不区分大小写匹配
//查询字符串中是否包含a
var reg = /a/i,
reg2 = /a/;
var str = 'BAC';

console.log(reg.test(str)) //true
console.log(reg2.test(str)) //false
  1. m : 多行搜索


方法

对于正则来说,可以有两种类型的调用方法进行:一个是正则对象上的方法,一个是string对象上的方法。

正则对象上的方法

正则对象上的方法有testexec

1.test方法

用于检测一个字符串是否匹配某个模式。

RegExpObject.test(string)

参数为一个字符串。如果匹配返回true,不匹配返回false。

var reg = /abc/,
str = 'abcedf';

console.log(reg.test(str)); //true



2.exec方法

用来检索字符串中的正则表达式的匹配。

RegExpObject.exec(string)

参数为一个字符串。当匹配时,返回一个数组。否则返回null。

返回的数组中,第0个元素为匹配的字符,第1个元素(如果有子表达式的话)为RegExpObject的第1个字表达式相匹配的的字符,第2个元素(如果有的话)为RegExpObject的第2个字表达式相匹配的的字符,以此类推。除此之外返回的数组还包含两个属性indexinputindex为第0个元素匹配字符的第一个字符的位置,input为被检索的原字符string。

var reg = /([a-z])[a-z]{1,3}/;
var str = 'ac abcd eabc abcdes 23abc';

console.log(reg.exec(str)) //["ac", "a", index: 0, input: "ac abcd eabc abcdes 23abc", groups: undefined]

还有另外一种情况,当RegExpObject为全局检索时,第一次检索时匹配的开始位置为0,即RegExpObject的属性lastIndex为0,第二次检索会从第一次匹配成功的字符的下一个位置开始检索,此时lastIndex为下一个位置,第三次检索会从第二次匹配成功的字符的下一个位置开始检索,以此类推,直到最后没有匹配返回null。

var reg = /([a-z])[a-z]{1,3}/,
reg2 = /([a-z])[a-z]{1,3}/g;
var str = 'ac abcd eabc abcdes 23abc';

console.log(reg.exec(str)) //["ac", "a", index: 0, input: "ac abcd eabc abcdes 23abc", groups: undefined]
console.log(reg.lastIndex) //0
console.log(reg.exec(str)) //["ac", "a", index: 0, input: "ac abcd eabc abcdes 23abc", groups: undefined]
console.log(reg.lastIndex) //0

console.log(reg2.lastIndex); //0
console.log(reg2.exec(str)); //["ac", "a", index: 0, input: "ac abcd eabc abcdes 23abc", groups: undefined]
console.log(reg2.lastIndex); //2
console.log(reg2.exec(str)); //["abcd", "a", index: 3, input: "ac abcd eabc abcdes 23abc", groups: undefined]

因此可以在循环中反复调用exec获得全部的匹配信息:

var reg2 = /([a-z])[a-z]{1,3}/g;
var str = '2ac abcd';
var result;
while((result = reg2.exec(str)) !== null){
console.log(result);
console.log(reg2.lastIndex)
}

//["ac", "a", index: 1, input: "2ac abcd", groups: undefined]
//3
//["abcd", "a", index: 4, input: "2ac abcd", groups: undefined]
//8


string对象上的方法

string对象上支持正则的方法有:search、match、replace、split

1.search

用于检索字符串中与正则表达式匹配的字符的位置。

stringObject.search(regexp)

如果找到匹配的字符串则返回匹配的第一个字符的位置,否则,返回-1。

var reg = /\d/,
str = 'hello 2 world';

console.log(str.search(reg)); //6



2.match

用于检索字符串中匹配的字符。

stringObject.match(regexp)

如果检索到匹配的字符,则返回一个数组,否则,返回null。

当regexp不是全局匹配时,match方法返回的数组和exec方法没用全局匹配返回的数组相同。第0个元素为匹配的字符,第1个元素(如果有子表达式的话)为RegExpObject的第1个字表达式相匹配的的字符,以此类推。还有indexinput属性。

当regexp是搜索时,match方法返回的包含所有匹配的字符的数据。

var reg = /([a-z])[a-z]{1,3}/,
reg2 = /([a-z])[a-z]{1,3}/g;
var str = 'ac abcd eabc abcdes 23abc';

console.log(str.match(reg)); //["ac", "a", index: 0, input: "ac abcd eabc abcdes 23abc", groups: undefined]

console.log(str.match(reg2)); // ["ac", "abcd", "eabc", "abcd", "es", "abc"]



3.replace

用于替换正则表达式匹配的字符。

stringObject.replace(regexp,string/function)

第一个参数为正则表达式,第二个参数为替换文本或函数,返回的是替换之后的新字符串。

如果regexp是全局检索的,那么replace方法将会替换所有匹配的子串。否则,它只替换第一个匹配的子串。

var reg = /xxx/,
reg2 = /xxx/g;
var str = 'hello xxx , welcom to xxx world';

console.log(str.replace(reg , 'John')); //"hello John , welcom to xxx world"

str.replace(reg2 , 'John'); // "hello John , welcom to John world"

第二个参数中可以使用具有特殊意义的$符号匹配:

$1、$2、... : 与regexp中第1、第2子表达式相匹配的文本

$& : 与regexp相匹配的子串

$` : 位于匹配字串左侧的文本

$' : 位于匹配字串右侧的文本

$$ : 直接量符号

var reg = /(\w+) (\w+)/,
str = 'Hi,Jackie Chan,123';

console.log(str.replace(reg , '$2 $1')); //"Hi,Chan Jackie,123"

console.log(str.replace(reg , '$& , welcome')); //"Hi,Jackie Chan , welcome,123"

console.log(str.replace(reg , '$`')); //"Hi,Hi,,123"

console.log(str.replace(reg , `$'`)); //"Hi,,123,123"

console.log(str.replace(reg , `$$`)); //"Hi,$,123"

如果第二个参数是函数的话,函数的入参如下:

match : 与regexp相匹配的子串,同上$&

p1,p2... : 与regexp中第1、第2子表达式相匹配的文本,同上$1、$2、...

offset : 匹配到子串第一个字符的位置

string : 被匹配的原字符串

var reg = /(\w+) (\w+)/,
str = 'Hi,Jackie Chan,123';

str.replace(reg , function(match , p1 , p2 , offset , string){
console.log('match:' , match) //match: Jackie Chan
console.log('p1:' , p1) //p1: Jackie
console.log('p2:' , p2) //p2: Chan
console.log('offset:' , offset) //offset: 3
console.log('string:' , string); //string: Hi,Jackie Chan,123

return `${p2} ${p1} , welcome`;
}) //"Hi,Chan Jackie , welcome,123"


4.split

根据正则表达式匹配的子串分割成数组。

stringObject.split(regexp , howmany);

第一个参数为正则表达式,第二个参数为指定返回数组的最大长度。split方法返回的是一个数组。

返回的数组中是不包含匹配的子串,如果想要包含匹配的子串,需要给正则加上捕获括号。

var reg = /\,/,
reg2 = /(\,)/,
str = 'hello,how,where';

console.log(str.split(reg)); //["hello", "how", "where"]

console.log(str.split(reg2)); //["hello", ",", "how", ",", "where"]


匹配特殊用法

反向引用

将捕获括号内匹配的内容保存到一个以数字编号的组里,再使用该数字编号,则是反向引用。

比如想要匹配日期,正则表达式为/(\d+)[./-](\d+)[./-](\d+)/,可以匹配日期格式1980.09.07、1980/09/07、1980-09-07,那么可以匹配为1980/09.07,不是我们想要的结果,我们想要的日期分隔符能够统一,那么可以使用反向引用实现。

var reg = /(\d+)([./-])(\d+)\2(\d+)/;

console.log(reg.test('2018/09/07')); //true
console.log(reg.test('2018/09.07')); //false

反向引用的数字编号\nn为第n个捕获括号。