正则表达式在平时工作中非常常见,工作中用到的大部分正则都是去网上搜索得来的,再复杂一点看着文档费时费力的拼凑一下。所以觉得很有必要对一些常用的正则内容进行一个归纳和总结。
概念: Regular Expression使用单个字符串来描述、匹配一系列符合某个语法规则的字符串。说简单了就是按照某种规则去匹配符合条件的字符串。
RegExp对象
javaScript中通过内置对象 RegExp 支持正则表达式,有两种方法实例化 RegExp 对象:
1.字面量
2.构造函数
1.字面量
假设你需要把一句英文里面的小写is匹配成大写的 IS,可以这样做:
var reg = /is/;
var text = 'He is a boy, This is a dog. Where is she?';
var result = text.replace(reg,'IS');
console.log(result) //He IS a boy, This is a dog. Where is she?
这样就把第一个英文单词'is'替换成了'IS',假如你想把该句中所有的单词'is'都替换成'IS',应该这样写:
var reg = /is/g;
var text = 'He is a boy, This is a dog. Where is she?';
var result = text.replace(reg,'IS');
console.log(result) //He IS a boy, ThIS IS a dog. Where IS she?
在正则的末尾加上'g'就好,'g'表示global,是全局匹配的意思。'g'是正则表达式的一个修饰符,修饰符有:
- 'g': global 全文搜索,不添加的话,搜索到第一个停止
- 'i': ignore case 忽略大小写,默认大小写敏感
- 'm': multiple 多行搜索,检测字符串中的换行符,主要是影响字符串开始标识符^和结束标识符$的使用
但是这样的话也改变了this中is的大小写,这显然不是我们想要的,所以这个时候可以这样做
var reg = /\bis\b/g;
var text = 'He is a boy, This is a dog. Where is she?';
var result = text.replace(reg,'IS');
console.log(result) //He IS a boy, This IS a dog. Where IS she?
'\b':匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。这里的正则在'is'的前后都有'\b',这样就只能匹配单词'is'了。
2.构造函数
倘若你需要使用构造函数的方式实例化正则,则上面的字面量形式可以改成这样:
var reg = new RegExp('\\bis\\b','g');
var text = 'He is a boy, This is a dog. Where is she?';
var result = text.replace(reg,'IS');
console.log(result) //He IS a boy, This IS a dog. Where IS she?
用这种方式就不需要'/'符号开始和结尾以表示是正则了。但是里面的''等特殊字符需要用''转义。
“\”:将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\ \”匹配“\”而“\ (”则匹配“(”。
元字符
正则表达式由两种基本字符类型组成:
- 原义文本字符,即代表它原本含义的字符
- 元字符,元字符是在正则表达式中有特殊含义的非字母字符,例如上文提到的'\b',表示匹配单词边界,并不是匹配'\b',在正则中主要存在这些特殊字符:*,+,?,$,^,.,|,,(,),{,},[,]
字符类(字符集合)
一般情况下,正则表达式一个字符对应字符串一个字符
但是更多的时候,我们匹配的并不是某个字符,而是符合一系列特征的字符串。这时候,我们就可以使用元字符'[]'来构建一个简单的类,所谓类是指符合某些特性的对象,一个泛指,而不是特指某个字符,例如:表达式'[abc]'把字符a或b或c归为一类,表达式可以匹配这样的字符。
var reg = /[abc]/g;
var text = 'a1b2c3d4';
var result = text.replace(reg,'X');
console.log(result); //X1X2X3d4
字符类取反
使用元字符'^'创建 反向类/负向类。反向类的意思是不属于类的内容,表达式'[^abc]'表示不是字符a或b或c的内容,例如:
var reg = /[^abc]/g;
var text = 'a1b2c3d4';
var result = text.replace(reg,'X');
console.log(result); //aXbXcXXX
范围类
倘若我们需要用字符类匹配数字,按照前面的匹配方式,书写可能会很麻烦,需要这样:'[0123456789]',对于 a 到 z 的字符更是如此。
为此,正则表达式给我们提供了范围类,我们可以使用[a-z]来连接两个字符,表示从a到z的任意字符,这是一个闭区间,包含 a 和 z 本身。
var reg = /[a-z]/g;
var text = 'a1b2c3d4z9';
var result = text.replace(reg,'Q');
console.log(result); //Q1Q2Q3Q4Q9
可以发现,这样就方便了许多。此外,在'[]'组成的类的内部是可以连写的[a-zA-Z],这样就形成了大写字母小写字母完全匹配:
var reg = /[a-zA-Z]/g;
var text = 'a1b2c3d4z9ASDFHDFH';
var result = text.replace(reg,'Q');
console.log(result); //Q1Q2Q3Q4Q9QQQQQQQQ
预定义类及边界
预定义类
匹配一个 ab+数字+任意字符 的字符串:
正则表达式提供预预定义类来匹配常见的字符类,让我们书写更方便。
| 字符 | 等价类 | 含义 |
|---|---|---|
| . | [^\r\n] | 除了回车符和换行符之外的所有字符 |
| \d | [0-9] | 数字字符 |
| \D | [^0-9] | 非数字字符 |
| \s | [\t\n\x0B\f\r] | 空白符 |
| \S | [^\t\n\x0B\f\r] | 非空白符 |
| \w | [a-zA-Z_0-9] | 单词字符(字母、数字、下划线) |
| \w | [a-zA-Z_0-9] | 单词字符(字母、数字、下划线) |
| \W | [^a-zA-Z_0-9] | 非单词字符 |
var reg = /ab\d./; //之前我们可能会这样写:ab[0-9][^\r\n]
var text = 'absdlkjflab91323';
var result = text.replace(reg,'AAAA');
console.log(result); //absdlkjflAAAA323
边界
除了预定义类,正则表达式还提供了几个常用的边界字符。
| 字符 | 等价类 |
|---|---|
| ^ | 以xxx开始 |
| $ | 以xxx结束 |
| \b | 单词边界 |
| \B | 非单词边界 |
我们在第一个例子中用到过'\b'单词边界,这里我们做一个跟上面第一个例子相反的,只把'This'中的'is'替换为'IS'
var reg = /\Bis\b/g;
var text = 'He is a boy, This is a dog. Where is she?';
var result = text.replace(reg,'IS');
console.log(result) //He is a boy, ThIS is a dog. Where is she?
在类'[]'中'^'表示取反,但是不在类中的时候'^'表示以xxx开始,'$'表示以xxx结束,这两个边界字符一般放在正则的开始和结束位置。
量词
倘若我们希望匹配一个连续出现20次的数字的字符串,通过我们之前学习的知识,我们可能会写出连续20个'\d'。假如20次你还可以接受,那100次,1000次,甚至更多次,你怎么办? 为了解决这个问题,正则表达式引入了量词的概念,下面是一些量词和他们的含义:
| 字符 | 含义 |
|---|---|
| ? | 出现零次或一次(最多出现一次) |
| + | 出现一次或者多次(至少出现一次 |
| * | 出现零次或者多次(任意次) |
| {n} | 出现n次 |
| {n,m} | 出现n到m次 |
| {n,} | 至少出现n次 |
var reg = /\d{4}[/-]\d{2}[/-]\d{2}/g;
var text = '2018-02-23,2018/02/24,2018~02/25';
var result = text.replace(reg,'匹配正确日期格式');
console.log(result);//匹配正确日期格式,匹配正确的日期格式,2018~02/25
分组
假如我们有这么一个场景:匹配字符串 Byron 连续出现3次的场景,根据前面所学,我们可能会这样写:Byron{3}。
但是这样是错误的,试试你会发现只有Byronnn才能匹配成功,即最后的n重复了3次,并不能匹配整个单词重复三次的情况:
var reg = /Byron{3}/g;
var text = 'ByronByronByronnn';
var result = text.replace(reg,'0');
console.log(result);//ByronByron0
那么,我们要怎么匹配Byron连续出现3次的情况呢,这时候,正则表达式的分组'()'就帮我们解决了这个问题:
var reg = /(Byron){3}/g;
var text = 'ByronByronByronnn';
var result = text.replace(reg,'0');
console.log(result);//0nn
或
有时候,我们可能会需要在匹配时用到或者的关系,利用之前的'[]'字符类(字符集合)可能只能匹配单个字符的或者关系,比如匹配a或b,你可以这样写:'[ab]',但是如果你需要匹配的是一整个单词的或者关系呢,这个时候'[]'就不好使了。这时候,我们用'|'可以达到或的效果:
//匹配单词Byron或者Casper
var reg = /Byron|Casper/g;
var text = 'ByronCasper'
var result = text.replace(reg,'X');
console.log(result);//XX
//匹配Byr+on或Ca+sper
var reg = /Byr(on|Ca)sper/g;
var text = 'ByronsperByrCasper'
var result1 = text.replace(reg,'X');
console.log(result1);//XX
反向引用
这样一个需求:把日期'2015-12-25'替换成'12/25/2015'
这时候,正则的反向引用就可以取到作用了。表达式在匹配时,表达式引擎会将小括号 "( )" 包含的表达式所匹配到的字符串记录(分组捕获)下来。在获取匹配结果的时候,小括号包含的表达式所匹配到的字符串可以单独获取。
在js中正则匹配成功的字符串可以用$1表示第一次匹配成功,$3表示第三次匹配成功的字符,以此类推至$99)。于是,上面的例子就可以这样写了:
var reg = /(\d{4})-(\d{2})-(\d{2})/g;
var text = '2015-12-25'
var result = text.replace(reg,'$2/$3/$1');
console.log(result);//12/25/2015
忽略分组
在上面的反向引用中,我们默认是根据'()'全部捕获记录为$1~$99的,倘若我们想忽略某个捕获要怎么办呢?
不希望捕获某些分组,只需要在分组内加上'?:'就可以了。
var reg = /(?:Byron)(\d{4})-(\d{2})-(\d{2})/g;
var text = 'Byron2016-12-05'
var result = text.replace(reg,'$2/$3/$1');
console.log(result);//12/05/2016
对象属性
思考: var reg1 = /\w/; var reg2 = /\w/g;那么reg1.test('a')和 reg2.test('ab')的结果分别是什么?
我们在用正则表达式相关的方法时,经常会用到正则表达式相关的一些对象属性,下面我们总结一下正则表达式相关的对象属性:
- golbal: 是否全文搜索,默认false
- ignore case: 是否大小写敏感,默认false
- multiline: 多行搜索,默认false
- lastIndex: 是当前表达式匹配内容的最后一个字符的下一个位置
- source: 正则表达式的文本字符串
其中前面三个我们在上文中已经提到过了,source的话,我们一起结合起来看看代码:
var reg1 = /\w/;
var reg2 = /\w/gim;
console.log(reg1.global);//false
console.log(reg1.ignoreCase);//false
console.log(reg1.multiline);//false
console.log(reg2.global);//true
console.log(reg2.ignoreCase);//true
console.log(reg2.multiline);//true
console.log(reg1.source);//\w
console.log(reg2.source);//\w
支持正则表达式的 String 对象的方法
支持正则表达式的 String 对象的方法有:
- search: 检索与正则表达式相匹配的值
- match: 找到一个或多个正则表达式的匹配。
- replace: 替换与正则表达式匹配的子串。
- split: 把字符串分割为字符串数组。
search
search() 方法用于检索字符串中指定的子字符串,或检索与正则表达式匹配的子字符串
语法为stringObject.search(regexp),结果返回 stringObject 中第一个与 regexp 相匹配的子串的起始位置index,如果没有找到任何匹配的子串,则返回 -1。需要注意的是,search() 方法不执行全局匹配,它将忽略修饰符'g',并且总是从字符串的开始进行检索。
var str = 'a1b2c3d4';
console.log(str.search('1')); //1
console.log(str.search('10')); //-1
console.log(str.search(/b2/)); //2
console.log(str.search(/\w\d/g)); //0
console.log(str.search(/\w\d/g)); //0 忽略'g',执行多次未返回不同结果
match
match() 方法将检索字符串,以找到一个或多个与 RegExp 匹配的文本,在 RegExp 是否有修饰符'g'影响很大。它返回指定的值,而不是字符串的位置。
语法为stringObject.match(searchvalue)或stringObject.match(regexp),结果返回存放匹配结果的数组。该数组的内容依赖于 regexp 是否具有全局标志 g。
1.非全局调用
- 如果 regexp 没有标志 g ,那么 match() 方法就只能在字符串中执行匹配一次
- 如果没有找到任何匹配文本,返回 null
- 否则它将返回一个数组,其中存放了与它找到的匹配文本有关的信息:
- 数组的第 0 个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本。
var reg3 = /\d(\w)\d/;
var str = '1a2b3c4d5e';
var arr = str.match(reg3);
console.log(arr);// [1a2, a]
全局调用
全局调用就和 exec() 不同了:
- 如果 regexp 具有标志 g 则 match() 方法将执行全局检索,找到字符串中所有匹配的子字符串
- 没有找到任何匹配的子字符串则返回 null
- 如果找到了一个或者多个匹配字串,则返回一个数组
- 数组元素中存放的是字符串中所有匹配的字串,而且也没有index属性和input属性
var reg4 = /\d(\w)(\w)\d/g;
var str = '$1az2bb3cy4dd5ee';
var arr = str.match(reg4)
console.log(arr); // ["1az2", "3cy4"]
split
我们经常用它把字符串分割为数组。但是你可能不知道,我们在一些复杂情况下我们可以使用正则表达式解决
var str = 'a,b,c,d';
var arr = str.split(',');
console.log(arr); //['a','b','c','d']
<!--那如果是这样的字符串呢-->
var str = 'a1b2c3d';
var arr = str.split(/\d/);
console.log(arr); //['a','b','c','d']
其实,我们在用 split() 分割字符','的时候,split() 方法也是把',' 隐士转换成正则'/,/'的, search() 方法和 replace() 方法也是一样的。
replace
replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。 语法为stringObject.replace(regexp/substr,replacement),结果返回一个新的字符串,是用 replacement 替换了 regexp 的第一次匹配或所有匹配之后得到的。