# 3、RegExp 对象



# 一、 概述

正则表达式(regular expression)是一种表达文本模式,通过给定模式匹配文本。JS 的正则是参考了 Perl5 建立的

新建正则表达式的方式有两种,

(1)字面量,以斜杠表示开始和结束。

var reg = /xyz/;

(2)RegExp 构造函数。

var reg = new RegExp("xyz");

第一种方式直观便利,而且第一种是在引擎编译代码时新建,第二种是在代码运行时新建。所以实际应用中,基本采用字面量定义正则表达式。

RegExp构造函数还可以接受第二个参数,表示修饰符。

var regExp = new RegExp("xyz", i);
// 等价于
var regExp = /xyz/i;

# 二、实例属性

# 2.1 RegExp.prototype.ignoreCase

查看是否设置了i 修饰符

# 2.2 RegExp.prototype.global

查看是否设置了g 修饰符

# 2.3 RegExp.prototype.multiline

查看是否设置了m 修饰符

# 2.4 RegExp.prototype.flags

列出所有修饰符

# 2.5 RegExp.prototype.lastIndex

返回一个整数,表示下一次开始搜索的位置。该属性可读写

# 2.6 RegExp.prototype.source

返回正则表达式的字符串形式(不包括反斜杠),该属性只读。

例子:

var reg = /abc/gim;

reg.ignoreCase; // true
reg.global; // true
reg.multiline; // true
reg.flags; // "gim"

reg.lastIndex; // 0
reg.source; // "abc"

# 三、实例方法

# 3.1 RegExp.prototype.test()

查看实例对象的 test()方法返回一个布尔值,表示当前模式是否能匹配字符串。

/cat/.test("cats and dogs"); // true
  • 带有g 修饰符时,可以通过lastIndex属性开始搜索位置,正则表达式内部会记住上一次的 lastIndex 的属性
var str = "_x234_x_5555_xx"; // 找出所有x位置的索引  --- 1、6、13、14
var reg = /x/g;
reg.lastIndex; // 0 从0开始
reg.test(str); // true
reg.lastIndex; // 2 从2开始检索
reg.test(str); // true
reg.lastIndex; // 7 从7开始检索
reg.test(str); // true
reg.lastIndex; // 14 从14开始检索
reg.test(str); // true
reg.lastIndex; // 15 从15开始检索
reg.test(str); // false
reg.lastIndex; // 0 从0开始检索

所以索引就是lastIndex - 1;
  • 案例:结合上面的方法和 substr 实现替换文本中特定文字的功能,文字颜色自定义 --- 代码位置/code/RegExp/reg1.html

# 3.2 RegExp.prototype.exec()

  • 遇到匹配的返回一个数组,否则返回 null
var s = "_x_x";
var reg1 = /x/;
var reg2 = /y/;
reg1.exec(s); // ["x"]
reg2.exec(s); // null
  • 如果正则有圆括号代表组匹配,会返回匹配多次后的数组
var r1 = /_(x)/;
r1.exec("_x"); // ["_x", "x", index: 0, input: "_x_x", groups: undefined]

先整体匹配一次得到“_x”,在括号中的匹配一次得到"x"。index 是最开始匹配到的索引,input 是原字符串

var r = /a(b+)a/;
var arr = r.exec("_abbba_aba_");

arr; // ["abbba", "bbb"]

arr.index; // 1
arr.input; // "_abbba_aba_"

利用g修饰符允许多次匹配的特点,可以用一个循环完成全部匹配。

var reg = /a/g;
var str = "abc_abc_abc";
while (true) {
  var match = reg.exec(str);
  if (!match) break;
  console.log("#" + match.index + ":" + match[0]);
}
// #0:a
// #4:a
// #8:a

案例:重写替换文字

var str = "你怎么这么美呢,你美的不可方物,让我不忍打断";
var reg = /美/g;
var result = "";
function test(str1) {
  const res = reg.exec(str1);
  if (!res) {
    return;
  } else {
    const txtArr = [...res.input];
    txtArr[res.index] = "丑";
    result = yy.join("");
    test(result);
  }
  return result;
}
const aa = test(str); // "你怎么这么丑呢,你丑的不可方物,让我不忍打断"
  • 比较 test()方法和 exec() 的不同:(1)返回结果不同:test 返回布尔;exec 返回数组。 (2)匹配结束返回的判断条件不同:test 返回 false,lastIndex 为 0;exec 返回的是 null,

  • 相同点是:一次只能返回一次的结果可以配合”g“修饰符循环遍历出所有匹配值及索引。可以设置lastIndex属性设置从第几位开始匹配

# 四、字符串的实例方法

# 4.1 String.prototype.match()

字符串实例的 match 方法进行正则匹配,返回所有(需要配合 g)匹配到结果的数组,成员是所有的子字符串。lastIndex属性设置无效从是从弟一个字符开始

var s = "_x_x";
var r1 = /x/;
var r2 = /y/;
s.match(r1); // ['x']
s.match(r2); // null

var reg = /x/g;
s.match(reg); // ["x","x"]

var r = /a|b/g;
r.lastIndex = 7;
"xaxb".match(r); // ['a', 'b']
r.lastIndex; // 0

字符串对象search方法,返回第一个满足条件的陪陪结果在整个字符串中的位置。没有匹配返回-1

var str = "qwert"
str.search(/w/)  //1
=> str.search("w")  // 1
str.search(/o/)  // -1

# 4.3 String.prototype.replace()

字符串对象的replace方法可以替换匹配的值。接受两个参数,第一个是正则表达式,便是搜索模式,第二个是替换的内容。

str.replace(search, replacement);

正则表达式如果不加g那就替换第一个匹配成功的值,否则替换所有成功的值

var str = "你好美啊,美女"; // 替换成你好帅啊,帅哥
str.replace(/美/g, "帅").replace(/女/, "哥");
console.log(str); //  你好帅啊,帅哥

案例:消除字符串首尾两端的空格。

var str = " #id div.class "
(1) => str.trimStart().trimEnd()
(2) => str.replace(/^\s+|\s+$/g,'')

replace方法的第二个参数可以使用美元$,用来指代所替换的内容。

  • $&: 匹配的子字符串
  • $`:匹配解构前面的文本。
  • $': 匹配文本后面的文本
  • $n: 匹配成功后的第 n 组内容,n 是从 1 开始的自然数
  • $$: 待指美元$.
// "你好,小明" 改成 "小明,你好"

"hello world".replace(/(\w+)\s(\w+)/, "$2 $1");
// "world hello"

"abc".replace("b", "[$`-$&-$']");
// "a[a-b-c]c"
  • 案例:查找替换一段文字中的指定文字并高亮,替换
    --- 代码位置/code/RegExp/reg3.html

# 4.3 String.prototype.split()

字符串对象split方法按照正则规则分割字符串,返回一个由分割后的各部分组成的数组。第一个参数是正则表达式,第二个是返回数组的最大成员数

str.split(separator, [limit]);
// 非正则分割 // "你好,小明" 改成 "小明,你好"
"你好,小明".split(',').reverse().join()  "小明,你好"

// 正则分割去掉多余的空格
"s sa ".trim()
'a  , b, c'.split(/  , *|, */).join()   // 通过结构匹配成数组在转化成文本

// 返回最大成员数
'a  , b, c'.split(/  , *|, */,2)  // ['a','b']

案例: split() 实现文字替换非常简单

"你好美啊,美的很".split(/美/).join("丑");

# 五、匹配规则

# 5.1 字面量字符和元字符

某个字符只表示它字面上的含义就是字面量字符比如

/美/.test("好美啊"); // true

除了字面量字符还有一部分字符有特殊含义,不代表字面的意思,我们叫他们"院子赋",主要有以下几个。

(1) 点字符( . )

点字符匹配任意的一个的字符。(除换行回车行分隔符,端分隔符)

/c.t/  //  匹配中间任何字符如"cot1_","c-t",但是多字符"coot"不可以
/c..t/.test('cddt')  // true

**(2) 位置字符 **

位置字符用来提示字符所处的位置,主要有两个

  • ^: 表示字符串开始的位置
  • $:表示字符串结束的位置
// 要匹配的内容的句首必须与字面量的值
/^/.test("美羊羊,你好美啊!")  // true

// 要匹配的内容的句尾必须与字面量的值
/^/.test("美羊羊,你好美")  // true

// 句首到句尾只能出现美
/^美$/.test('美')

// 如果不加,就是匹配哪里有都可以
//.test('你好美!') // true

**(3) 选择符 ( | ) **

数线在正则表达式中表示‘或者(OR)’, 即cat|dog 表示匹配 cat 或者 dog

/^cat|dog/.test("cat145"); // true

# 5.2 转义符

正则中有很多特殊含义的元字符,如果要匹配他们本身,就需要在他们前面加反斜杠"/",比如匹配+ 需要写成\+

/1+1/.test('1+1')
/1\+1/.test('1+134')  // true

注意:如果使用 RegExp 构造函数的话,需要两个\\因为字符串内部反斜杠也需要转译,然后正则再转译一次

const reg = new RegExp("1\\+1");
reg.test("1+1"); // true

const reg = new RegExp("1+1");
reg.test("1+1"); // false

像这样需要转译的字符一共有 12 个:^.[$()|*+?{\

# 5.3 特殊字符

对一些不能打印的特殊字符,也提供了表达方式。

常用的有以下几个

  • \n匹配换行键
  • \r匹配回车键
  • \t匹配 tab
  • \f匹配换页符
  • \0匹配 null 字符

# 5.4 字符类

字符类(class)[] 表示从一系列字符中选择,匹配到其中一个即可。如[xyz]表示能匹配到 x、y、z 中的任意一个即可。

/[你好美啊]/.test("我不知道你怎么这么美呢?"); // true
// 字符串中含有表达式中的任意一个字符即可

**(1) 脱字符 ( ^ ) **

^在元字符中表示位置开头,但是用到字符类[]中第一个位置出现表示脱字符

比如 [ ^xyz ] 意思是一定要包含 x、y、z 之外的其他字符

/[^test]/.test('tghjkl')  // true
/[^test]/.test('123')     // true
/[^test]/.test('test')    // false
/[^]/.test('匹配任意包括换行符')

(2) 连字符 ( - )

连字符为连续序列的字符提供简写形式( - ) , 比如字符的连续范围,[abc]可以写成[a-c],

[0123456789]可以写成[0-9], 同理 A-Z 可以写成[A-Z]

/[0-9A-Xa-z]/.test('2sS')  // true
/[^0-9A-Xa-z]/.test('1qZ-')  // true

# 5.5 预定义模式

预定义模式是某些常见模式的简写

  • \d 代表 0~9 之间任意数字,相当于[0-9]
  • \D代表 0 ~ 9 以外的字符,相当于[ ^0-9 ]
  • \w 代表的是任意字母、数字和下划线,相当于[A-Za-z0-9]
  • \W代表的是除所有任意字母、数字和下划线之外,相当于[ ^A-Za-z0-9 ]
  • \s匹配空格(包含换行符、制表符、空格符),相当于[ \t\r\n\v\f]
  • \S匹配非空格的字符,相当于[^ \t\r\n\v\f]
  • \b匹配词的边界
  • \B匹配非词的边界,即词的内部
// \s 的例子
/\s/.test('dfg ggg')  // true

// \b 的例子 匹配的词必须独立存在于开头,能区分出来这个词 同理 \B 相反
/\bword/.test('hello world') // true
/\bert/.test('2345c.ertddddd') // true
/\Bert/.test('2345c.ertddddd') // false

一般正则表达式遇到\n会停止匹配

var html ="Hello\n world";
/.*/.exec(html)[0]  // Hello

因为点字符( . ) 不匹配换行符,导致匹配结果不符合原意。使用[\s\S]代替一切字符

var html = "Hello\n world";
/[\s\S]*/.exec(html)[0]; //Hello\n world"

# 5.6 重复类

模式的精准匹配次数,使用大括号( {} ) 表示, { n } 表示恰好重复 n 次,{ n, }表示至少重复 n 次,{ n, m} 表示至少重复 n 次,最多重复 m 次。

/lo{2,}k/.test('look') // true
/lo{2,5}k/.test('looook') // true

# 5.7 量词类

量词符用来设定某个模式出现的次数。

  • ?问号表示某个模式出现 0 次或 1 次,等同于{0,1}
  • *星号表示某个模式出现 0 次或者多次,等同于{0,}
  • +加号表示某个模式出现了 1 次或者多次,等同于{1,}
 // t 出现了0次或者1次用? 不出现可以
 /t?est/.test('test')  // true
 /t?est/.test('est')   // true
 // t 出现了1次或多次 用+  必须出现最少一次
 /t+est/.test(est)     // false
 /t+est/.test(tttest)  // true
 // t 出现了0次或多次 用* 不出现也可以
 /t*est/.test(es)  // true
 /t*es/.test(es)   // true

# 5.8 贪婪模式

?、*、+ 三个量词符,默认都是最大可能匹配,即匹配知道下一个字符不满足匹配规则为止。这种被称为贪婪模式。

var s = "aaa";
s.match(/a+/); // ["aaa"]
s.match(/a?/); // ["a"]
s.match(/a*/); // ["aaa"]

如果匹配时想改成非贪婪模式只需要在后面加一个问号'?', 一旦条件满足,就不再往下匹配。如+? 匹配到后出现 1 次停止。*? 匹配到出现 0 次停止 ?? 匹配到 0 次

"abb".match(/ab*/); // ["abb"]
"abb".match(/ab*?/); // ["a"]

"abb".match(/ab+/); // ["abb"]
"abb".match(/ab+?/); // ["ab"]

"abb".match(/ab?/); // ["ab"]  贪婪模式下匹配到1次,非贪婪匹配到0次
"abb".match(/ab??/); // ["a"]

# 5.9 修饰符

修饰符表示模式的附加规则,放在正则模式的最尾部。

可以单个使用也可以多个一起使用

var reg = /test/i;
var reg = /test/gi;

(1)g 修饰符(global)

正则默认匹配成功就会停止向下继续匹配。g修饰符表示全局匹配。正则对象将匹配全部符合条件的结果,主要用于搜索和替换。

var reg = /b/;
var str = "abba";
reg.test(str) // true
reg.test(str) // true
reg.test(str) // true
...

var reg = /b/g;
var str = "abba";
reg.test(str) // true  r1.lastIndex = 2
reg.test(str) // true  r1.lastIndex = 3
reg.test(str) // false r1.lastIndex = 0

不加 g 每次都匹配到第一个成功后停止,而加 g 之后,每次成功后,下次匹配从下一个开始匹配,直到全部匹配完。lastIndex 为 0

(2)i 修饰符(ignoreCase)

默认情况下,正则对象区分字母的大小写,加上 i 修饰符以后,忽略大小写。

/abc/.test("ABC") / // false
  abc /
  i.test("ABC"); // true

(3)m 修饰符(multiline)

m 修饰符表示多行模式,会修改^和$的行为。默认情况下不加m修饰符时,^和$匹配字 符串的开始和结尾处,加上 m 修饰符之后, 会匹配每一行的行首和行尾,多用于\n 换行 中。

/^ab/.test("er/nab") / // true
  ab$ /
  m.test("er\nab\n"); // true

# 5.10 组匹配

  • ()用来表示分组匹配
/ab+/.test(abb) / // true
  ab +
  /.test(abab)  / / true;
  • 用来分组捕获
"abc".match(/(.)b(.)/); // ["abc", "a", "c", index: 0, input: "abc", groups: undefined]

2020-3-01