JS中正则表达式

时间:2023-03-10 07:02:28
JS中正则表达式

正则表达式用于对字符串模式匹配及检索替换,是对字符串执行模式匹配的强大工具。简单来说正则表达式就是处理字符串的,我们可以用它来处理一些复杂的字符串。

1、创建方式

pattern(模式):描述了表达式的模式

modifiers(修饰符) :用于指定不区分大小写、全局匹配和多行匹配(i、g、m),当没有时默认为区分大小写只匹配第一个

1.1、实例创建方式

var reg = new RegExp(pattern,modifiers);  //构造函数创建方式,pattern 和 modifiers 均为字符串

示例代码:

var regx = new RegExp("^\\d+[a-z]*$","g");  //没有字面量方法两边的 / 字符,参数都是字符串形式
var reg = new RegExp(/\d{2}/g);       //这样创建也可以,但是此时没有第二个参数。在 es6 中可以用字符串做第二个参数修饰符,但此时第二个参数会覆盖掉前面的所有的修饰符。

1.2、字面量创建方式(推荐使用)

var reg = /pattern/modifiers;    // 字面量创建方式

示例代码:

var regx = /^\d+[a-z]*$/;
var regx = /^\d+[a-z]*$/gi;

1.3、字面量创建方式和构造函数创建方式的区别

(1)字面量创建方式使用元字符不需要转义,而实例创建方式使用元字符需要转义

var reg1 = new RegExp('\w');   //   /w/   只匹配 w 字母
var reg2 = new RegExp('\\w') // /\w/ 匹配单词字符
var reg3 = /\w/; // /\w/ 匹配单词字符,在字面量写法中不用转义

(2)字面量创建方式不能用表达式,实例创建方式可以

var regParam = 'cm';
var reg1 = new RegExp(regParam+'1');
var reg2 = /regParam/;
console.log(reg1); // /cm1/
console.log(reg2); // /regParam/

2、修饰符

2.1、i(不区分大小写)

var str = "Visit RUnoob";
var patt1 = /runoob/;
var patt2 = /runoob/i;
console.log(patt1.test(str)) //false
console.log(patt2.test(str)) //true

2.2、g(全局匹配)

即查找所有匹配而非在找到第一个匹配后停止

var str="Is this all there is?";
var patt1=/is/g;
console.log( str.match(patt1) ); // ["is", "is"]

2.3、m(执行多行匹配)

多行匹配在需要匹配的字符串里有换行符时凸显出它的意义。如果没有用多行匹配,那么不管多少换行符,该字符串就只视为一行,只有一对头和尾。如果使用了多行匹配,那么每一行就都有一对头和尾,使用 ^ 和 $ 进行匹配时结果就不一样。

var str="This an\n good";
var reg=/an$/;
var reg2=/an$/m;
console.log( reg.test(str) ); //false
console.log( reg2.test(str) ); //true var str2="This an\n good an";
var reg3 = /an$/mg;
console.log( str2.match(reg3) ); // ["an", "an"]

3、元字符

  详情参考:http://www.runoob.com/jsref/jsref-obj-regexp.html

.  : 查找单个字符,除了换行 \n 和行结束符。
\d : 0-9之间的任意一个数字 \d只占一个位置
\D : 除了\d,即查找非数字字符
\w : 查找单词字符。
\W : 除了\w,查找非单词字符
\s : 空格或者空白等
\S : 除了\s,查找非空白字符。
\n : 匹配换行符
\b : 匹配边界 字符串的开头和结尾 空格的两边都是边界 => 不占用字符串位数 \ : 转义字符
| : 或者,一般用在分组中
() : 分组
^ : 限定开始位置 => 本身不占位置
$ : 限定结束位置 => 本身不占位置
[a-z] : 任意字母 []中的表示任意一个都可以
[^a-z] : 非字母,[]中^代表除了
[abc] : abc三个字母中的任何一个,[^abc]除了这三个字母中的任何一个字符

代表次数的量词元字符

* : 0到多个
+ : 1到多个
? : 0次或1次,可有可无
{n} : 正好n次;
{n,} : n到多次
{n,m} : n次到m次

4、中括号 [ ] 和圆括号 ()

4.1、[ ] 中括号用于匹配某个范围内的某一个字符

[abc]    匹配方括号之间的任一字符。
[^abc] 匹配任何不在方括号之间的字符。
[0-9] 匹配任何从 0 至 9 的数字。
[a-z] 匹配任何从小写 a 到小写 z 的字符。
[A-Z] 匹配任何从大写 A 到大写 Z 的字符。
[A-z] 匹配任何从大写 A 到小写 z 的字符。

[ ]中的字符一般没有特殊含义,比如 + 就表示匹配字符 '+' ,. 就表示一个 . 字符而不是元字符的含义,当然咱们还是可以用转义的,比如用[\.]来表示 . 字符而不是元字符,为了统一,建议还是都用转义。

但是像 \w 这样的元字符在[]里的含义还是没有变。[ ]中不会出现匹配两位数的情况,都是表示匹配其中的任一字符。

var str1 = 'abc';
var str2 = 'dbc';
var str3 = '.bc';
var reg = /[ab.]bc/; //此时的.就表示.
reg.test(str1) //true
reg.test(str2) //false
reg.test(str3) //true

4.2、分组

( ) :表示分组,只要正则中出现了小括号那么就会形成一份分组

var reg = /(\d{2})/;
console.log( reg.test('12') ); //true 这里reg中的(\d{2})就表示一个分组,匹配两位数字

在分组中,通过 | 隔开的几个候选表达式为并列关系,可以把 | 理解为或的意思

var reg = /I come from (hunan|hubei|zhejiang)/;
reg.test('I come from hunan'); //true
reg.test('I come from hubei'); //true

4.3、分组的类别:

  • 捕获型 ():使用的比较多,只有这种分组才会暂存分组匹配到的字符串,然后加以引用
  • 非捕获型 (?:)
  • 正向前瞻型 (?=):表示需匹配的字符后面的位置是分组里的字符,但用match方法返回匹配到的字符不包括分组里的字符。即零宽度先行断言,在第八标题讲。
  • 反向前瞻型 (?!):即零宽度先行否定断言,在第八标题讲

(1)捕获型:被正则表达式捕获(匹配)到的字符串会被暂存起来,其中,由分组捕获到的字符串会从1开始编号,我们可以引用这些字符串:

var reg = /(\d{4})-(\d{2})-(\d{2})/;
var dateStr = '2018-04-18';
reg.test(dateStr); //true
RegExp.$1 //
RegExp.$2 //
RegExp.$3 //

应用:结合replace方法做字符串自定义替换

var dateStr = '2018/04/18';
var reg = /(\d{4})\/(\d{2})\/(\d{2})/; //注意这里的/是需要用\转义的
dateStr = dateStr.replace(reg, '$1-$2-$3') //"2018-04-18"

反向引用:在正则表达式里进行引用

var reg = /(\w{3}) is \1/
reg.test('kid is kid') // true
reg.test('dik is dik') // true
reg.test('kid is dik') // false
reg.test('dik is kid') // false //如果引用了越界或者不存在的编号的话,就被被解析为普通的表达式
var reg = /(\w{3}) is \6/;
reg.test( 'kid is kid' ); // false
reg.test( 'kid is \6' ); // true

(2)非捕获型分组 (?:):有的时候只是为了分组并不需要捕获的情况下就可以使用非捕获型分组

var reg = /(?:\d{4})-(\d{2})-(\d{2})/
var date = '2004-22-33'
reg.test(date)
RegExp.$1 //
RegExp.$2 //

5、正则表达式对象(RegExp)的方法

5.1、exec

返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。

rexp.exec() 方法比较复杂,全局匹配和不全局匹配结果不一样,当用到分组时结果又不一样。

//不全局匹配的情况下:
var str = 'abc123cba456aaa789';
var reg = /\d+/;
console.log( reg.exec(str) ) //["123", index: 3, input: "abc123cba456aaa789"];
console.log(reg.lastIndex) //0 // [ "123",index:3,input:"abc123cba456aaa789" ] 中,
// "123" 表示我们捕获到的字符串
// index:3 表示捕获到的字符串的起始位置
// input 表示原有的字符串

当我们用exec进行捕获时,如果正则没有加 'g' 标识符,则exec捕获的每次结果都是同一个,当正则中有 'g' 标识符时捕获的结果就不一样了,再来看刚刚的例子

var str = 'abc123cba456aaa789';
var reg = /\d+/g; //此时加了标识符g
console.log(reg.lastIndex) // lastIndex : 0 console.log(reg.exec(str)) // ["123", index: 3, input: "abc123cba456aaa789"]
console.log(reg.lastIndex) // lastIndex : 6 console.log(reg.exec(str)) // ["456", index: 9, input: "abc123cba456aaa789"]
console.log(reg.lastIndex) // lastIndex : 12 console.log(reg.exec(str)) // ["789", index: 15, input: "abc123cba456aaa789"]
console.log(reg.lastIndex) // lastIndex : 18 console.log(reg.exec(str)) // null
console.log(reg.lastIndex) // lastIndex : 0 // 每次调用exec方法时, 捕获到的字符串都不相同
// lastIndex: 这个属性记录的就是下一次捕获从哪个索引开始,当未开始捕获时,这个值为0。
// 如果当前次捕获结果为null。 那么lastIndex的值会被修改为0.下次从头开始捕获。
// 而且这个lastIndex属性还支持人为赋值。

exec的捕获还受分组()的影响

let str = 'aaabbb';
let reg = /(a+)(b+)/;
let reg2 = /(a+)(?:b+)/; console.log( reg.exec(str) ); //["aaabbb", "aaa", "bbb", index: 0, input: "aaabbb", groups: undefined]
console.log( reg2.exec(str) ); //["aaabbb", "aaa", index: 0, input: "aaabbb", groups: undefined] //exec执行匹配的时候, 会先返回整体匹配值, 再分别返回按照正则表达式中由括号扩起来的小分组进行匹配的值。?: 可以取消返回该分组的匹配值。

5.2、test()

rexp.test( string ) 判断字符串是否匹配正则表达式,匹配就返回 true ,否则 false。

6、一些支持正则表达式的 String 对象的方法

6.1、search()

str.search( rexp ) 用于检索与正则表达式相匹配的子字符串。若找到就返回第一个匹配的字符串的起始位置,如果没有找到则返回 -1。

var str="Visit RuRoob!";
console.log( str.search(/R/) ); //

6.2、match()

str.match( rexp ) 在字符串内找到一个或多个正则表达式的匹配的子串。

如果不是全局匹配,那么 match() 方法就只执行一次匹配,如果匹配到,那么返回的值跟 exec 方法没有使用全局匹配返回的值一样,即 [str, index:number, input: str, groups]。如果没有找到任何匹配的文本, 则返回 null;

如果使用了全局匹配,匹配到的话它将返回一个数组,其中存放了所有匹配到的子串,若没有找到则返回 null。

var str = "The rain in SPAIN stays mainly in the plain";
console.log(str.match(/aaa/)); //null
console.log( str.match(/ain/) ); //["ain", index: 5, input: "The rain in SPAIN stays mainly in the plain", groups: undefined]
console.log(str.match(/ain/g)); // ["ain", "ain", "ain"]

6.3、replace()

str.replace(searchvalue,newvalue) 返回一个用 newvalue 替换掉 searchvalue 的新字符串,该方法不会改变原始字符串。

let str = "Visit Microsoft! Visit Microsoft!";
console.log( str.replace(/Microsoft/, "Runoob") ); //Visit Runoob! Visit Microsoft!
console.log( str.replace(/Microsoft/g, "Runoob") ); //Visit Runoob! Visit Runoob!

7、正则的特性

贪婪性:所谓的贪婪性就是正则在捕获时,每一次会尽可能多的去捕获符合条件的内容。如果我们想尽可能的少的去捕获符合条件的字符串的话,可以在量词元字符后加 ?

懒惰性:懒惰性则是正则在成功捕获一次后不管后边的字符串有没有符合条件的都不再捕获。如果想捕获目标中所有符合条件的字符串的话,我们可以用标识符g来标明是全局捕获

var str = '123aaa456';
var reg = /\d+/; //只捕获一次,一次尽可能多的捕获
console.log( str.match(reg) ); // ["123", index: 0, input: "123aaa456"]
reg = /\d+?/g; //用 ?解决贪婪性,用 g 懒惰性
console.log( str.match(reg) ); // ["1", "2", "3", "4", "5", "6"]

8、零宽断言

用于查找在某些内容(但并不包括这些内容)之前或之后的东西,如 ^,$ 那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。

在使用正则表达式时,捕获的内容前或者后必须是特定的内容,而我们又不想捕获这些特定内容的时候,零宽断言就可以派上用场了。

  • 零宽度先行断言 (?=exp):表示需匹配的字符后面的位置是分组里的字符,但用match方法返回匹配到的字符不包括分组里的字符。
  • 零宽度先行否定断言 (?!exp):与上面相反,应该不是分组里的字符。
  • 零宽度后行断言 (?<=exp):表示需匹配的字符前面的位置是分组里的字符,但用match方法返回匹配到的字符不包括分组里的字符。
  • 零宽度后行否定断言 (?<!exp):与上面相反,前面应该不是分组里的字符。

零宽度先行断言 (?=exp) 意思是字符出现的位置的右边必须匹配到exp这个表达式。

var str = "i'm singing and dancing";
var reg = /\b(\w+(?=ing\b))/g;
console.log(str.match(reg)) // ["sing", "danc"],“断言”之中的部分 (?=ing\b),是不计入返回结果的。 // 注意一点零宽度的意思就是说断言里面的不算在需要匹配的字符里面,它只是表达一种位置关系
var str = 'abc';
var reg = /a(?=b)c/;
console.log(reg.test(str)); // false
var reg2 = /a(?=b)b/;
console.log(reg2.test(str)); // true
// 第一个看起来似乎是正确的,实际上结果是false
// reg中a(?=b)匹配字符串'abc' 字符串a的右边是b这个匹配没问题, 接下来reg中a(?=b)后边的c匹配字符串时是从字符串'abc'中ab的中间开始匹配的,
// 这个相当于 / ac / 匹配'abc', 显然结果是false了

零宽度先行否定断言(?!exp) 意思是字符出现的位置的右边不能是exp这个表达式。

var str = 'nodejs';
var reg = /node(?!js)/;
console.log( reg.test(str) ) // false

零宽度后行断言(?<=exp) 这个就是说字符出现的位置的前边是exp这个表达式。

var str = '¥998$888';
var reg = /(?<=\$)\d+/;
console.log(reg.exec(str)) //

零宽度后行否定断言 (?<!exp) 这个就是说字符出现的位置的前边不能是exp这个表达式。

var str = '¥998$888';
var reg = /(?<!\$)\d+/;
console.log(reg.exec(str)) //

参考博文https://www.cnblogs.com/wancheng7/p/8906015.html  、https://www.cnblogs.com/chenmeng0818/p/6370819.html