JS正则表达式详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS正则表达式详解相关的知识,希望对你有一定的参考价值。

参考技术A

RegExp是JS的正则表达式对象,实例化一个RegExp对象有 字面量 构造函数 2种方式。

字面量实例化RegExp对象

var reg=/js/gi;

开始和结束的斜线/是正则表达式的边界,//中间的文本是正则表达式文本,后面的gi是正则表达式的修饰符。

构造函数实例化RegExp对象

var reg=new Regex(‘js’,’gi’);

第一个参数是正则表达式文本,第二个参数是正则表达式修饰符。

global:RegExp 对象是否具有标志 g。

ignoreCase:RegExp 对象是否具有标志 i。

lastIndex:一个整数,标示开始下一次匹配的字符位置。

multiline:RegExp 对象是否具有标志 m。

source:正则表达式的源文本。

RegExp 对象有 3 个方法:test()、exec() 以及 compile()。

test()

test() 方法检索字符串中的指定值。返回值是 true 或 false。

示例:

var reg=/js/gi;

var result=reg.test(‘I like js’);

结果:true

exec()

exec() 方法检索字符串中的指定值。返回值是被找到的值。如果没有发现匹配,则返回 null。

示例:

var reg=/js/gi;

var result=reg.exec(‘I like JS’);

结果:JS

compile()

compile() 方法用于改变 RegExp。

compile() 既可以改变检索模式,也可以添加或删除第二个参数。

g:global全文搜索,如果不添加此参数,搜索到第一个匹配就会停止

i:ignore case 不区分大小写,默认是区分大小写的

m:multiple lines 多行搜索,默认只搜索第一行

正则表达式文本部分包含 原义文本字符 元字符, 其中的原义文本字符代表字符本身意义的字符,如abc123之类的字符。

元字符代表有特殊含义非字母字符,如\\b、\\w、\\d、+、*、? 等。

常用的元字符

更多的JS正则元字符请参考: http://www.runoob.com/regexp/regexp-metachar.html

一般情况下正则表达式中的一个字符对应字符串中的一个字符,如表达式ab的含义是ab。

表达式中用 [] 来定义一个字符类,表示可以匹配[]里面的这类字符,是一个泛指,而不是一一对应的关系。

如表达式[abc123],表示有其中一个字符串都可以匹配。

在字符类里面使用横线-连接2个数字或者字母就可以构建一个范围类,如[a-zA-z0-9-]表示可以匹配26个大小写字母和0-9的全部数字以及横线-。

注意,要匹配横线”-“,必须把横线放在最后面。

预定义类可以理解为JS默认给我们写好的范围类,让我们可以使用一个简单的元字符来使用它。如”\\w”就是一个预定义类,它等价于范围类[A-Za-z0-9_];“.”可以匹配除 “\\n” 之外的任何单个字符。

边界包含2种,一种是以字符串开始或结尾的边界,另一种是单词边界

量词表示可以匹配连续多次的元字符

JS正则表达式默认是贪婪模式匹配,它会以最多匹配原则进行查找,非贪婪模式可以让表达式以最少匹配原则进行查找。

非贪婪模式只需要在量词后面加上”?”即可,如”123456789″字符串想以3个数字为一组进行匹配表达式写法/\\d3,5?/g。

表达式中用 () 来定义一个分组,使元字符可以作用于一个表达式字符串组合,如/(js|php)3/gi。

表达式中的每一个分组匹配的内容都是一个可以捕获的变量,可以使用$1、$2、$3… 来取值,如表达式/(\\d4)-(\\d2)-(\\d2)/中包含$1、$2、$3。如果想忽略分组匹配的内容,需要在分组前面增加“?:”,就可以得到分组匹配之外的内容。

前瞻就是正则表达式后面加上断言部分,它不但要匹配表达式部分,还需要满足断言部分,匹配的结果不会包含断言部分。

正向前瞻 exp(?=assert) 如 “\\w(?=\\d)”

负向前瞻 exp(?!assert) 如 “\\w(?!\\d)”

match()

stringObject.match(regexp)

match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。

如果 regexp 没有标志 g,那么 match() 方法就只能在 stringObject 中执行一次匹配。

如果没有找到任何匹配的文本, match() 将返回 null。

否则,它将返回一个数组,其中存放了与它找到的匹配文本有关的信息。该数组的第 0 个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本。

search()

stringObject.search(regexp)

search() 方法不执行全局匹配,它将忽略标志 g。它同时忽略 regexp 的 lastIndex 属性,并且总是从字符串的开始进行检索,这意味着它总是返回 stringObject 的第一个匹配的位置。

replace()

stringObject.replace(regexp/substr,replacement)

字符串 stringObject 的 replace() 方法执行的是查找并替换的操作。它将在 stringObject 中查找与 regexp 相匹配的子字符串,然后用 replacement 来替换这些子串。如果 regexp 具有全局标志 g,那么 replace() 方法将替换所有匹配的子串。否则,它只替换第一个匹配子串。

replacement 可以是字符串,也可以是函数。如果它是字符串,那么每个匹配都将由字符串替换。但是 replacement 中的 $ 字符具有特定的含义。如下表所示,它说明从模式匹配得到的字符串将用于替换。

split()

stringObject.split(separator,howmany)

如果 separator 是包含子表达式的正则表达式,那么返回的数组中包括与这些子表达式匹配的字串(但不包括与整个正则表达式匹配的文本)

正则表达式语法语意测试工具: https://regexper.com/

JavaScript系列文章:详解正则表达式之一

正则表达式是一个精巧的利器,经常用来在字符串中查找和替换,JavaScript语言参照Perl,也提供了正则表达式相关模块,开发当中非常实用,在一些类库或是框架中,比如jQuery,就存在大量的正则表达式,所以说学好正则表达式,是提高开发技能的一项基本要求。那么今天博主就来详细总结一下正则表达式的相关知识,希望不熟悉的同学们,也能够掌握正则表达式的原理及应用。

在JS中,创建正则表达式有两种方式,一种是字面量方式,一种是构造器方式,如下所示:

var regex = /\\w+/;

// 或者

var regex = new RegExp(\'\\\\w+\');

大家也许注意到,使用字面量要比构造器简洁得多,\\w表示一个word,匹配单个字母、数字或下划线,而使用RegExp构造器时,我们的正则变为了"\\\\w",这是因为要在字符串中表示一个反斜杠\\,我们需要对其转义,也就是在前面再加一个转义字符\\。相信大家都知道,要在字面量正则中表达一个匹配反斜杠\\的正则,只需写成\\\\这样,但在字符串中表达这个正则,则是"\\\\\\\\"这个样子的,这是因为字符串中前两个表示一个反斜杠\\,后两个也表示一个反斜杠\\,最终在正则层面,结果还是\\\\。

对于上面两种创建形式,都可以加上一些后缀修饰符,这些修饰符可以单个使用,也可以组合起来使用:

/\\w+/g; // global search
/\\w+/i; // ignore case
/\\w+/m; // multi-line
/\\w+/u; // unicode
/\\w+/y; // sticky

/\\w+/gi;
new RegExp(\'\\\\w+\', \'gi\');

从英文注释来看,相信大家都大概都略知一二了,需要注意的是u和y修饰符,它们是ES6新增的特性,u表示启用Unicode模式,对于匹配中文特别有用,而y是sticky,“粘连”的意思,表示下次匹配时目标紧随当前匹配项,这个我们后面会介绍。

正则相关方法

有了正则表达式对象了,如何使用呢?JS中的正则和字符串在原型中均提供相应的方法,先来看看正则原型中的两个方法:

RegExp.prototype.test(str);
RegExp.prototype.exec(str);

上面的test()和exec()方法都需传入一个字符串,对这个字符串进行搜索和匹配,不同的是,test()方法会返回true或false,表示字符串和正则是否匹配,而exec()方法在匹配时返回一个匹配结果数组,如果不匹配,则只返回一个null值,下面来看看两者的差异:

// RegExp#test()

var regex = /hello/;
var result = regex.test(\'hello world\');   // true

// RegExp#exec()

var regex = /hello/;
var result = regex.exec(\'hello world\');   // [\'hello\']

对于exec()方法,如果正则中含有捕获组,匹配后则会出现在结果数组中:

// (llo)是一个捕获组

var regex = /he(llo)/;
var result = regex.exec(\'hello world\');   // [\'hello\', \'llo\']

开发当中,test()方法一般用于用户输入验证,比如邮箱验证,手机号验证等等,而exec()方法一般用于从特定内容中获取有价值的信息,比如从用户邮箱输入中获取其ID和邮箱类型,从手机号中获取此号码的归属地等等。

字符串相关方法

上面是正则原型中的两个方法,现在来看看字符串原型中都提供了哪些可用的方法:

String.prototype.search(regexp);
String.prototype.match(regexp);
String.prototype.split([separator[, limit]]);
String.prototype.replace(regexp|substr, newSubStr|function);

先来说说String#search()方法,它会根据正则参数对字符串进行匹配搜索,如果匹配成功,就返回第一次匹配处的索引,如果匹配失败,则返回-1。

// String#search()

\'hello world\'.search(/hello/);    // 0

\'hello world\'.search(/hi/);       // -1

String#match()方法跟RegExp#exec()方法相似,会返回结果数组,所不同的是,如果String#match()的正则参数中含有全局标记g,则结果中会只出现匹配的子串,而忽略捕获组,这一点与RegExp#exec()有些出入。且看下面代码:

// String#match()

\'hello hello\'.match(/he(llo)/);     // [\'hello\', \'llo\']

// String#match()遇到全局g修饰符时会舍弃捕获组

\'hello hello\'.match(/he(llo)/g);    // [\'hello\', \'hello\']

// RegExp#exec()仍旧包含捕获组

/he(llo)/g.exec(\'hello hello\');     // [\'hello\', \'llo\']

所以,如果需要总是将捕获组作为结果返回,应该使用RegExp#exec()方法,而不是String#match()方法。

接下来说说String#split()方法,这个方法用于将字符串分割,然后返回一个包含其子串的数组结果,其中separator和limit参数都是可选的,separator可指定为字符串或正则,limit指定返回结果个数的最大限制。如果separator省略,该方法的数组结果中仅包含自身源字符串;如果sparator指定一个空字符串,则源字符串将被以字符为单位进行分割;如果separator是非空字符串或正则表达式,则该方法会以此参数为单位对源字符串进行分割处理。下面代码演示了该方法的使用:

// String#split()

\'hello\'.split();                  // ["hello"]
\'hello\'.split(\'\');                // ["h", "e", "l", "l", "o"]
\'hello\'.split(\'\', 3);             // ["h", "e", "l"]

// 指定一个非空字符串

var source = \'hello world\';
var result = source.split(\' \');   // ["hello", "world"]

// 或者使用正则表达式

var result = source.split(/\\s/);  // ["hello", "world"]

如果separtor是一个正则表达式,并且正则中包含捕获组,则捕获组也会出现在结果数组中:

// String#split() 正则捕获组

var source = \'matchandsplit\';

var result = source.split(\'and\');     // ["match", "split"]

var result = source.split(/and/);     // ["match", "split"]

// 正则中含捕获组

var result = source.split(/(and)/);   // ["match", "and", "split"]

最后来介绍一下String#replace()方法,它会同时执行查找和替换两个操作。

从上面的函数签名来看,该方法会接受两个参数:第一个参数可以是一个正则表达式,也可以是一个字符串,它们都表示将要匹配的子串;第二个参数可以指定一个字符串或是一个函数,如果指定一个字符串,表示这个字符串将会替换掉已匹配到的子串,如果指定一个函数,则函数的返回值会替换掉已匹配的子串。

String#replace()方法最终会返回一个新的已经过替换的字符串。下面分别演示了replace方法的使用:

// String#replace()

var source = \'matchandsplitandreplace\';

var result = source.replace(\'and\', \'-\');  // "match-splitandreplace"

// 或者

var result = source.replace(/and/, function() {
    return \'-\';
});                                       // "match-splitandreplace"

从上面的代码中可以看到,\'and\'被替换成了\'-\',但我们同时也注意到,只有第一个\'and\'被替换了,后面的并没有被处理。这里我们就需要了解,String#replace()方法只对第一次出现的匹配串进行替换,如果我们需要全局替换,需要将第一个参数指定为正则表达式,并追加全局g修饰符,就像下面这样:

// String#replace() 全局替换

var source = \'matchandsplitandreplace\';

var result = source.replace(/and/g, \'-\'); // "match-split-replace"

var result = source.replace(/and/g, function() {
    return \'-\';
});                                       // "match-split-replace"

初学者看到上面的代码,可能会觉得疑惑,对于第二个参数,直接指定一个字符串也挺简单的嘛,我们为何要使用一个函数然后再返回一个值呢。我们看看下面的例子就知道了:

// String#replace() 替换函数的参数列表

var source = \'matchandsplitandreplace\';

var result = source.replace(/(a(nd))/g, function(match, p1, p2, offset, string) {
  
    console.group(\'match:\');
    console.log(match, p1, p2, offset, string);
    console.groupEnd();

    return \'-\';
});                                       // "match-split-replace"

上面代码中,第一个参数是正则表达式,其中包含了两个捕获组(and)和(nd),第二个参数指定一个匿名函数,其函数列表中有一些参数:match, p1, p2, offset, string,分别对应匹配到的子串、第一个捕获组、第二个捕获组、匹配子串在源字符串中的索引、源字符串,我们可以称这个匿名函数为“replacer”或“替换函数”,在替换函数的参数列表中,match、offset和string在每一次匹配时总是存在的,而中间的p1、p2等捕获组,String#replace()方法会根据实际匹配情况去填充,当然,我们还可以根据arguments获取到这些参数值。

下面是代码运行后的控制台打印结果:

现在来看,指定一个函数要比指定一个字符串功能强的多,每次匹配都能获取到这些有用的信息,我们可以对其进行一些操作处理,最后再返回一个值,作为要替换的新子串。所以推荐在调用String#replace()方法时,使用上面这种方式。

上面是String类与正则相关的常用方法,需要注意的是,String#search()和String#match()方法签名中参数均为正则对象,如果我们传递了其他类型的参数,会被隐式转换为正则对象,具体的步骤是先调用参数值的toString()方法得到字符串类型的值,然后调用new RegExp(val)得到正则对象:

// -> String#search(new RegExp(val.toString()))

\'123 123\'.search(1);        // 0
\'true false\'.search(true);  // 0

\'123 123\'.search(\'\\\\s\');    // 3

var o = {
    toString: function() {
        return \'\\\\s\';
    }
};
\'123 123\'.search(o);        // 3

// -> String#match(new RegExp(val.toString()))

\'123 123\'.match(1);         // ["1"]
\'true false\'.match(true);   // ["true"]

\'123 123\'.match(\'\\\\s\');     // [" "]

var o = {
    toString: function() {
        return \'1(23)\';
    }
};
\'123 123\'.match(o);         // ["123", "23"]

而split()和replace()方法不会将字符串转为正则表达式对象,对于其他类型值,只会调用其toString()方法将参数值转为字符串,也不会进一步向正则转换,大家可以亲自测试一下。

以上就是正则的相关基本知识及常用方法,限于篇幅原因,更多关于正则表达式的内容,博主会安排在下一篇中介绍和讲解,敬请期待。 

 

参考资料:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions

http://es6.ruanyifeng.com/#docs/regex

以上是关于JS正则表达式详解的主要内容,如果未能解决你的问题,请参考以下文章

js正则表达式 URL格式匹配详解

如何用正则表达式检测ip是不是正确,求详解

正则表达式详解

JS 正则表达式^$详解,脱字符^与美元符$同时写表示什么意思?

js正则表达式替换捕获内容

js字符串和正则表达式中的matchreplaceexec等函数详解