javascript 正则表达式

·  阅读 74

正则表达式在平时工作中非常常见,工作中用到的大部分正则都是去网上搜索得来的,再复杂一点看着文档费时费力的拼凑一下。所以觉得很有必要对一些常用的正则内容进行一个归纳和总结。

概念: 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 的第一次匹配或所有匹配之后得到的。