正则表达式

作者: 杜先生 | 发表于 2021-06-03 10:43 | 分类 web | 阅读 93 | 评论 0

正则表达式

1. 概念

正则:表达的是一种规则或者模式,用于规范字符串。

正则表达式:由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的字符串,表示一种文字模式,用于描述一种匹配规则,匹配一些列符合某个语法规则的字符串。

2. 创建正则表达式

在 Javascript 中 RegExp 类表示正则表达式,后续例子均为在 JS 运行环境下

2.1 采用 new 操作符

var pattern1 = new RegExp('pattern');
var pattern2 = new RegExp('pattern', 'gi');
// 检查类型
var type = Object.prototype.toString.call(pattern1);
console.log(type);  // [object RegExp]
console.log(pattern1);  // /pattern/
console.log(pattern2);  // /pattern/gi
var partten = new RegExp('^(a|b)\\w', 'g');
console.log(pattern);  // /^(a|b)\w/g

其中 new RegExp() 可以传递两个参数:

  • 参数1:需要进行检索或替换的指定字符串,对于需要使用的特殊符号,使用 \ 转义即可
  • 参数2:可选,模式修饰符,用于对检索或替换过程的限制

2.2 字面量表达式方式

var pattern1 = /pattern/;
var pattern2 = /pattern/gi;
var pattern3 = /^(a|b)\w/g;

写法为 使用两个反斜杠,中间任意字符。第二个斜杠后加一个或者多个模式修饰符,这里含义为 忽略大小写,全局匹配

3. 正则表达式组成

3.1 修饰符

修饰符 是影响整个正则规则的特殊符号,会对匹配结果和部分内置函数行为产生不同的效果,JavaScript 正则表达式中,包含以下三个修饰符:

参数含义作用
g全局匹配 
(global)
全局查找,对于一些特定的函数,将迭代完整的字符串,获得所有的匹配结果,而不仅仅在得到第一个匹配结果后就停止进行
i忽略大小写 
(ignore)
大小写不敏感
m多行匹配 
(multiline)
检测字符串中的换行符,主要是影响字符串开始标识符 ^ 和结束标识符 $ 的使用
  • 修饰符 g 的使用
var str = 'He is a boy. Where is he?'
var pattern = /is/g;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);  // ["is", "is"]
console.log(result2);  // He X a boy. Where X he?
  • 修饰符 i 的使用
var str = 'He is a boy. Is he?';
var pattern = /is/ig;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);  // ["is", "Is"]
console.log(result2);  // He X a boy. X he?
  • 修饰符 m 的使用
// 多行字符
var str = 
`@123
@456
@789`;
var pattern = /^@\d/gm;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);  // ["@1", "@4", "@7"]
console.log(result2);

// 结果为多行字符,每一个换行都会对开始标识符进行匹配
X23
X56
X89

3.2 元字符

正则表达式由两种基本字符类型组成

  • 原义(正常)文本字符:代表原本含义的字符,写什么就是表示匹配什么
  • 元字符:是一个或一组代替一个或多个字符的字符。简单来说就是在正则表达式中有特殊含义的非字母字符

常见的有:* + ? $ ^ . | \ () {} []

其他常见的见下表:

字符含义
\t水平制表符
\v垂直制表符
\n换行符
\r回车符
\0空字符
\f换页符
\cX与 X 对应的控制字符(Ctrl + X) \cZ --- ctrl + z 组合键

3.3 字符类

一般情况下正则表达式中一个字符对应匹配字符串中一个字符。如

var str = 'abc';
var result = str.match(/ab/g);
console.log(result);  // ['ab']

正则表达式中的 ab 就是匹配 ab。

正则图解

当希望匹配不是指定的某个而是某几个字符中的一个,就可以使用元字符 [] 来构建一个简单的集合类。

所谓类是指符合某些特性的对象,一个泛指,而不是特指某个字符。

var str = 'a1b2c3d4a5';
var result1 = str.match(/[abc]/g);
var result2 = str.replace(/[abc]/g, 'X');
console.log(result1);  // ["a", "b", "c", "a"]
console.log(result2);  // X1X2X3d4X5

表达式 [abc] 把字符 a 或 b 或 c 归为一类,只要匹配这三个其中一个即可。

正则图解

3.4 字符类取反

使用元字符 ^ 结合 [] 创建 反向类/负向类,反向类的意思是不属于某类的内容。

var str = 'a1b2c3d4a5';
var pattern = /[^abc]/g;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);  // ["1", "2", "3", "d", "4", "5"]
console.log(result2);  // aXbXcXXXaX

表达式 [^abc] 表示 不是字符 a 或者 b 或者 c 的内容

正则图解

3.5 范围类

正则表达式提供了 范围类

我们可以使用 [a-z] 来连接两个字符表示 从 a 到 z 的任意字符(只表示一个)。

这是个闭区间,也就是包含 a 和 z 本身

var str = 'a1b2c3d4e5';
var pattern = /[a-z]/g;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);  // ["a", "b", "c", "d", "e"]
console.log(result2);  // X1X2X3X4X5

表达式 [a-z] 表示 从 a 到 z 的任意一个字符

正则图解

在 [] 组成的类内部是可以连写的如 [a-zA-Z]

var str = 'a1b2c3d4e5A1B2B5JLJDLS';
var pattern = /[a-zA-Z]/g;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);  // ["a", "b", "c", "d", "e", "A", "B", "B", "J", "L", "J", "D", "L", "S"]
console.log(result2);  // X1X2X3X4X5X1X2X5XXXXXX

表达式 [a-zA-Z],表示 a-z 之间所有的大小写字母的其中一个

正则图解

  • 如果在范围类中想要匹配 - 符号或其他字符,则按照 ‘字符-字符’ 的规则从左向右的顺序进行组合,短横线两侧均有内容时则组合成范围类,否则以短横线进行匹配
var str = '2018-7-23';
var pattern = /[1-5-]/g;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);  // ["2", "1", "-", "-", "2", "3"]
console.log(result2);  // X0X8X7XXX

表达式 [1-5-],表示 1-5 之间任意整数和 - 符号中的一个

正则图解

3.6 预定义类

正则表达式提供 预定义类 来匹配常见的字符类

字符等价类含义
.[^\r\n]除了回车符合换行符之外的所有字符
\d[0-9]数字字符,digit 表示 0-9 之间的数字
\D[^0-9]非数字字符
\s[\t\n\x0B\f\r]空白符,space 空白,把什么隔开
\S[^\t\n\x0B\f\r]非空白符
\w[a-zA-Z_0-9]单词字符(字母/数字/下划线),word 单词
\W[^a-zA-Z_0-9]非单词字符

举例:

匹配一个 ab + 数字 + 任意字符 的字符串

ab[0-9][^\r\n]  => 简化为 ab\d.

3.7 边界

正则表达式还提供了几个常用的边界匹配字符

字符含义
\b单词边界,boundary 边界
\B非单词边界
^以...开始
$以...结束

匹配正常的单词边界

var str = 'He is a boy. This Is a dog. Where is she?';
var pattern = /\bis\b/g;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);  // ["is", "is"]
console.log(result2);  // He X a boy. This Is a dog. Where X she?

表达式 \bis\b,表示 is 的两侧要有单词边界,也就是只会匹配单词 is。所以 this 中 is 部分的就不会被匹配。

正则图解

匹配 以...开始

元字符的含义不是唯一的,在不同的场景下表示不同的含义 ^ 在类字符中表示取反,在类字符外表示 以...开始

// 正常匹配
var str = '@123@abc@';
var pattern1 = /@./g;
console.log(str.match(pattern1));         // ["@1", "@a"]
console.log(str.replace(pattern1, 'X'));  // X23Xbc@
// 必须以 @ 开头才会被匹配
var str = '@123@abc@';
var pattern = /^@./g;
console.log(str.match(pattern));         // ["@1"]
console.log(str.replace(pattern, 'X'));  // X23@abc@

表达式 ^@.,表示必须以 @ 作为开始字符,后面可以跟随一个任意字符。在中部及后面的 @ 就不会被匹配

正则图解

匹配 以...结束

注意:$符号书写在匹配字符的后面

// 正常匹配
var str = '@123@abc@';
var pattern = /.@/g;
console.log(str.match(pattern));         // ["3@", "c@"]
console.log(str.replace(pattern, 'X'));  // @12XabX
// 必须以 @ 结尾才会被匹配
var str = '@123@abc@';
var pattern = /.@$/g;
console.log(str.match(pattern));         // ["c@"]
console.log(str.replace(pattern, 'X'));  // @123@abX

表达式 .@$,表示必须以 @ 作为结束字符,前面可以是一个任意字符。在开始位置及中间的 @ 就不会被匹配

正则图解

多行字符

在 ES6 中可以书写多行字符,对于新的一行则认为是新的开始,所以就可以对应的进行多行匹配。

多行匹配需使用 多行匹配修饰符 m

// 多行字符
var mulStr = 
`@123
@456
@789`;
var pattern = /^@\d/gm;
console.log(mulStr.match(pattern));          // ["@1", "@4", "@7"]
console.log(mulStr.replace(pattern, 'X'));   
/* 替换结果仍为多行字符串
*  X23
*  X56
*  X89
*/

表达式 ^@\d,表示必须以 @ 作为开始字符,后面可以跟随一个数字。

正则图解

3.8 量词

正则表达式提供 量词 来匹配连续出现的统一类型的字符

字符含义
?出现零次或 1 次(最多出现 1 次)
+出现 1 次或多次(至少出现 1次)
*出现零次或多次(任意次)
{n}出现 n 次
{n,m}出现 n 到 m 次,两侧都是闭区间,包含 n 或 m 次
{n,}至少出现 n 次
{0,n}最多出现 n 次

注意:由于在正则表达式中每一个符号都表示匹配对应一个字符,在 {} 里面, 后面是不能使用空格隔开内容的,否则会将 {} 认为是普通的字符去进行匹配

如果需要连续匹配多个相同字符或类,可以使用量词进行省略简写

\d{20}   ===>  连续匹配数字 20 次

正则图解

贪婪模式

当正则表达式中包含能接受重复的限定符时,即存在数量范围要求时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。

var str = '12345678';
var pattern = /\d{3,6}/g;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);   // ["123456"]
console.log(result2);   // X78

正则图解

对于上述情况,数字连续出现 3 次到 6 次都能满足要求,即正则表达式在多种次数情况均能满足时,会尽可能多的匹配,直到匹配失败,这里会优先按照次数多的进行匹配,最后按照 6 次进行处理

非贪婪模式(懒惰模式)

当需要让正则表达式尽可能少的匹配,也就是说一旦成功匹配则不再继续尝试就是非贪婪模式。

做法跟简单,在量词后面加上 ? 即可

var str = '12345678';
var pattern = /\d{3,6}?/g;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);   // ["123", "456"]
console.log(result2);   // XX78

正则图解

对于上述情况,数字连续出现 3 次到 6 次都能满足要求,即正则表达式在多种次数情况均能满足时,在量词的后面追加一个 ? 符号,会尽可能少的进行匹配,这里会优先按照次数少的进行匹配,最后按照 3 次进行处理。

3.9 分组

所谓的分组就是使用小括号将一些字符包括起来,使其成为独立的逻辑域,那么就可以像处理一个独立单元一样去处理小括号的内容。

ab{3}

正则图解

以上正则表达式只能够重复 3 次 b。但是想重复 ab 两个字符,可以使用小括号来指定子表达式或者说分组来实现此功能,例如:

(ab){3}

正则图解

上面的正则就可以重复 ab 了。

正常分组

把单独的项目进行分组,以便合成子表达式,这样就可以像处理一个单独的字符那样,对其应用 |、 +、 * 或者 ? 等元字符来操作它们。

var str = 'I love javascript and java';
var pattern = /java(script)?/gi;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'X');
console.log(result1);   // ["javascript", "java"]
console.log(result2);   // I love X and X

表达式 java(script)?,表示匹配字符串 javascript 也可以匹配 java。将 script 划分为一组,使用重复量词 ? 控制数量,可以使前面的子表达式重复 0 次或者 1 次。

正则图解

捕获型分组

捕获型分组也可以成为 子表达式引用

正则表达语法中可以在同一个表达式中的后部引用前面的子表达式(分组)。对于子表达式的引用,并不是引用的匹配模式,而是对子表示匹配内容的引用。

分组(子表达式)反向引用

在相应的方法中使用正则表达式时,可以使用 $ 后面跟随数字 获取对应分组匹配的内容。此数字指定了小括号子表达式在正则表达式中的位置,例如 $1 是引用的第一个带有圆括号的子表达式,自然 $2 就是第二个带有圆括号的子表达式。

通过这些,可以很容易的实现匹配字符串格式的转化。

var str = '2015-12-25';
var pattern = /(\d{4})-(\d{2})-(\d{2})/g;
var result = str.match(pattern);
console.log(result);    // ["2015-12-25"]

// 替换为分组匹配内容,查看对应的分组
console.log(str.replace(pattern, '$1'));    // 2015
console.log(str.replace(pattern, '$2'));    // 12
console.log(str.replace(pattern, '$3'));    // 25
console.log(str.replace(pattern, '$4'));    // $4
console.log(str.replace(pattern, '$2/$3/$1'));    // 12/25/2015

通过 $0 无法获取整个表达式。另外如果编号超出了分组数量,则按照普通字符对待

由于子表达式是可以相互嵌套的,那么计算子表达式的位置的时候,只要确定左括号的位置即可。按照从左到右的顺序进行编号。

var str = 'Javascript';
var pattern = /(Java(script))/g;
console.log(str.replace(pattern, '$1'));    // Javascript
console.log(str.replace(pattern, '$2'));    // script

[正则图解](https://jex.im/regulex/#!flags=g&re=(Java(script)))

分组(子表达式)内部引用

在正则表达式内部可以通过在 "" 后面跟随数字实现分组内容的获取。此数字指定了小括号子表达式在正则表达式中的位置,例如 \1 是引用的第一个带有圆括号的子表达式,自然 \2 就是第二个带有圆括号的子表达式。

\0 表示整个正则表达式

表达式内部对对子表达式的引用一般是用来实施一条约束,例如

/(['"])[^'"]*\1/g

正则图解

通常情况下左右引号是相匹配的,如果前面是双引号,那么结尾也要是双引号,前面是单引号那么结尾也必须是单引号,并且字符串中间不能够再出现其他的引号。

var str = `I love "Javascript",  love 'Javascript'`;
var pattern = /(['"])[^'"]*\1/g;
console.log(str.match(pattern));   // [""Javascript"", "'Javascript'"]
console.log(str.replace(pattern, 'PHP'));   // I love PHP, I love PHP

非捕获型分组

非捕获性分组也就是 有些地方需要用到一对括号组成一个子表达式,但是又不想让他成为一个捕获性分组,也就是不想让这个分组被引用,进入编号序列中。通常在括号内部的前面加上?: 也就是 (?:pattern) 这样就变成了一个非捕获性分组。

var str = '2015-12-25';
var pattern = /(?:\d{4})-(\d{2})-(\d{2})/g;
var result = str.match(pattern);
console.log(result);    // ["2015-12-25"]

// 完全替换为分组匹配内容,查看对应的分组
console.log(str.replace(pattern, '$1'));    // 12
console.log(str.replace(pattern, '$2'));    // 25
console.log(str.replace(pattern, '$3'));    // $3
console.log(str.replace(pattern, '$2/$3/$1'));    // 25/$3/12

正则图解

分组会占用一定的系统资源,尤其是正则表达式较长的时候会降低匹配速度。有时候仅仅是为了设置一个分组,并不需要引用,那么使用非引用类型分组将会是一个良好的选择。

3.10 或

在正则表达式进行匹配时,在要求多个分支条件满足其中一个分支即可时,可以使用 符号 | 来达到 或 的要求。

在使用 或 时,注意优先级

var str = 'Lucy and Lily';
var pattern = /Lucy|Lily/g;
var result1 = str.match(pattern);
var result2 = str.replace(pattern, 'Jack')
console.log(result1);    // ["Lucy", "Lily"]
console.log(result2);    // Jack and Jack

表达式 Lucy|Lily,表示匹配 Lucy 单词 或者 Lily 单词

正则图解

在分组中也可以使用

Lu(cy|Li)ly

正则图解

表达式 Lu(cy|Li)ly,表示匹配 Lucyly 或者 LiLily 单词

3.11 零宽断言

零宽断言 用于查找在某些内容之前或之后的东西,但并不包括这些内容。是一种零宽度的匹配,它匹配的内容不会保存到匹配结果中,也不会占用 index 宽度,最终匹配的结果只是一个位置。这个位置应该满足一定的条件(即断言),因此它们被称为零宽断言。

断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。

零宽断言分为四类,大体分为 前瞻和后瞻。(也有叫先行,后发等)

名称正则语法含义其他
正向前瞻pattern(?=exp)目标字符出现的位置的右边必须匹配到 exp 这个表达式
负向前瞻pattern(?!exp)目标字符出现的位置的右边不能匹配到 exp 这个表达式
正向后瞻pattern(?<=exp)目标字符出现的位置的左边必须匹配到 exp 这个表达式Javascript 不支持
负向后瞻pattern(?<!exp)目标字符出现的位置的左边不能匹配到 exp 这个表达式Javascript 不支持

3.11.1 前瞻

前瞻 就是在正则表达式匹配到规则时,向前检查是否符合断言。

  • 正向前瞻语法为 pattern(?=exp),即在目标字符串的相应位置必须有 pattern 部分匹配的内容,但不作为匹配结果处理,更不会存储在缓冲区内供以后使用。

匹配执行顺序是:对要进行匹配的字符串中的最右侧(也就是从后往前查找)查找匹配 exp 规则的字符位置,匹配到后然后向前匹配 pattern 表达式,若无法匹配 pattern,则继续向前查找 exp 能够匹配的第二个字符位置,继续向前匹配 pattern 表达式,若能匹配则匹配返回相应结果。

var str = 'Hello, Hi, I am Hilary.';
var pattern = /H(?=i)/g;
var result = str.replace(pattern, 'T');
console.log(result);   // Hello, Ti, I am Tilary.

正向前瞻的作用,同样是字符 'H',但是只匹配 'H' 后面紧跟 'i' 的 'H'。

  • 负向前瞻语法为 pattern(?!exp),即在目标字符串的相应位置必须有 pattern 部分不能匹配的内容。

匹配执行顺序是:对要进行匹配的字符串中的最右侧(也就是从后往前查找)查找不能匹配 exp 规则的字符位置,符合要求后然后向前匹配 pattern 表达式,若无法匹配 pattern,则继续向前查找 exp 能够匹配的第二个字符位置,继续向前匹配 pattern 表达式,若能匹配则匹配返回相应结果。

var str = 'Hello, Hi, I am Hilary.';
var pattern = /H(?!i)/g;
var result = str.replace(pattern, 'T');
console.log(result);   // Tello, Hi, I am Hilary.

负向前瞻的作用,匹配字符 'H',但是后面不能跟着一个 'i'。

3.11.2 后瞻

由于 Javascript 中不支持后瞻,这里只分析执行顺序

后瞻跟前瞻恰恰相反。它的执行步骤是这样的:先从要匹配的字符串中的最左端找到第一个能够匹配 exp 规则的字符片段(也就是先行断言中的表达式)然后再使用 pattern 匹配其后面的表达式,若无法匹配则继续向后查找第二个能够匹配 exp 规则的字符片段,再使用 pattern 匹配其后面的表达式,若能匹配 则匹配。

3.11.3 前瞻的非捕获性

在使用前瞻时,使用了分组符号 (),但是不会被 '' 符号或者 '$' 符号进行捕获。

var str = 'Hello, Hi, I am Hilary.';
var reg = /H(?!i)/g;
var result = str.replace(reg, "T$1");
console.log(result);   // T$1ello, Hi, I am Hilary.

如果按照正常分组进行匹配,即使没有匹配到对应字符,后续反向引用也会显示空字符串 '',但是最后的结果之间将 '$1' 作为替换的字符处理。这是因为前瞻表达式根本就没有捕获,没有捕获也就没有引用。

4.正则表达式属性和相关方法

4.1 正则表达式实例属性

在 Javascript 中正则表达式是 内置对象 RegExp 的实例。每一个实例都有如下属性:

属性含义
global是否全文搜索,默认值 false
ignore case是否大小写敏感,默认值是 false
multiline多行搜索,默认值是 false
source正则表达式的文本字符串,不包含修饰符
flags正则表达式的修饰符
lastIndex是当前表达式匹配到内容的最后一个字符的下一个位置,也就是当前匹配到对应内容后下一次开始匹配的位置

关于 lastIndex 的补充解释:正则 /ab/g 在对字符串 'abcde' 进行匹配时,在匹配到对应的字符片段 ab 后会继续向后匹配,下一次从 c 的位置进行下一次匹配,所以 lastIndex 的值是 c 字符的索引值

var pattern = /\w/;
console.log(pattern.global);       // false
console.log(pattern.ignoreCase);   // false
console.log(pattern.multiline);    // false
console.log(pattern.source);       // \w
console.log(pattern.flags);        // ''
console.log(pattern.lastIndex);    // 0

在表达式不使用 修饰符 时对应的属性为 false

var pattern = /\w/gim;
console.log(pattern.global);       // true
console.log(pattern.ignoreCase);   // true
console.log(pattern.multiline);    // true
console.log(pattern.source);       // \w
console.log(pattern.flags);        // gim
console.log(pattern.lastIndex);    // 0

这些属性都是 只读属性,不能修改对应的值

4.2 正则表达式实例方法

正则表达式都是 构造函数 RegExp 的实例。所以实例方法均是继承于 RegExp 的原型中。

  • RegExp.prototype.test()

使用语法:

bool = pattern.test(str)

用于测试字符串参数中是否存在能够匹配正则表达式模式的字符串。即在 str 中是否存在 pattern 能够匹配到的内容。存在返回 true,否则返回 false

var pattern = /(a|b)/;
console.log(pattern.test('a'));   // true
console.log(pattern.test('$'));   // false

表达式 \w 用于对普通的单词字符进行匹配,字符 'a' 能够正常匹配出结果,返回结果为 true。而字符 '$' 则不可以,所以返回结果为 false。

但是如果添加了修饰符 g,在对同一个字符串进行多次验证时,对应返回的结果会不稳定,即有时返回为 true,有时为 false。

var pattern = /\w/g;
console.log(pattern.lastIndex);   // 0
console.log(pattern.test('ab'));  // true
console.log(pattern.lastIndex);   // 1
console.log(pattern.test('ab'));  // true
console.log(pattern.lastIndex);   // 2
console.log(pattern.test('ab'));  // false
console.log(pattern.lastIndex);   // 0
console.log(pattern.test('ab'));  // true
console.log(pattern.lastIndex);   // 1

这是因为由于存在修饰符 g,正则表达式会进行全局匹配,将字符串的整段进行一一对比,每次匹配后的结果都会作用在正则表达式本身上,修改对应的 lastIndex 值,以控制确定下一次匹配开始时的位置。

这里的执行顺序是:

  1. 在匹配到 a 时,表示字符串内部存在能够匹配正则表达式的内容,则返回 true,
  2. 由于存在修饰符 g,对应将内部的 lastIndex 的属性值变成字符 'a' 的索引值加 1, 也就是下一次匹配开始的位置,这里就是字符 'b' 的索引值。
  3. 正常匹配到 'b',返回 true。由于匹配结束,但是 lastIndex 的值会修改成字符 'b' 的索引值加 1,也就是 2。
  4. 正则表达式继续匹配时按照索引值 2 去查找下一个匹配的字符,自然无法找到,返回 false。将 lastIndex 的值修改为 0,结束全局匹配。
  5. 若继续匹配,则重复步骤 1-4。

若想始终得到准确结果,则可以使用如下方法:

var pattern = /\w/g;
while (ret = pattern.test('ab')) {
    console.log(ret);
    console.log(pattern.lastIndex);
}

依次输出: true, 1, true, 2

由于存在这个特征,所以在使用同一个表达式对多个字符串进行匹配或对同一个字符串进行多次匹配时,得到的结果可能有错误。但是,一般情况下,如果只是想得到是否有对应匹配的内容,是不需要添加 g 修饰符的,这样在匹配到一个结果后,就会停止匹配。

如果真的想进行全局匹配并且知道对应匹配的内容和位置,则可以使用 exec 方法。

  • RegExp.prototype.exec()

使用语法:arr = pattern.exec(str)

用于使用正则表达式模式对字符串执行搜索,并将更新全局 RegExp 对象的属性以反映匹配结果。如果没有匹配的文本则返回 null,否则返回一个结果数组,数组内容如下:

  • index 声明匹配文本的第一个字符的位置
  • input 存放被检索的字符串 string

非全局调用(不使用 g)

在正则表达式不进行全局匹配时(即不使用修饰符 g),表达式中的 lastIndex 属性不生效,始终为 0。返回的数组内容为:

  • 第一个元素是与正则表达式相匹配的文本
  • 第二个元素是与 RegExpObject 对象的第一个子表达式(也就是分组)相匹配的文本(如果有的话)
  • 第三个元素是与 RegExpObject 对象的第二个子表达式(也就是分组)相匹配的文本(如果有的话),以此类推
  • index 属性是与正则表达式相匹配的文本起始位置的索引值
var str = '1a2b3c4d5e';
var pattern = /\d(a|b)\w(c|d)\d/;
var result = pattern.exec(str);

console.log(pattern.lastIndex);   // 0
console.log(result.index);        // 2
console.log(result.toString());   // 2b3c4,b,c

console.log(result);  // ["2b3c4", "b", "c", index: 2, input: "1a2b3c4d5e", groups: undefined]

全局调用(使用 g)

在正则表达式进行全局匹配时(即使用修饰符 g),表达式中的 lastIndex 属性按照匹配顺序正常使用,在匹配到相应字符后修改为下一次匹配开始的索引值位置。返回的数组内容为:

  • 第一个元素是与正则表达式相匹配的文本
  • 第二个元素是与 RegExpObject 对象的第一个子表达式(也就是分组)相匹配的文本(如果有的话)
  • 第三个元素是与 RegExpObject 对象的第二个子表达式(也就是分组)相匹配的文本(如果有的话),以此类推
  • index 属性是与正则表达式相匹配的当前文本起始位置的索引值,在进行多次匹配对应到不同的位置
var str = '1a2b3c4d5e';
var pattern = /\d(\w)\d/g;
var ret = [];

while (result = pattern.exec(str)) {
    console.log(pattern.lastIndex);
    console.log(result.index);
    console.log(result);
    ret.push(result[0]);
}
console.log(ret);

// 循环输出结果为:
// 3
// 0
// ["1a2", "a", index: 0, input: "1a2b3c4d5e", groups: undefined]
// 以上是 匹配到 '1a2' 时的输出结果

// 4
// 7
// ["3c4", "c", index: 4, input: "1a2b3c4d5e", groups: undefined]
// 以上是 匹配到 '3c4' 时的输出结果

// ["1a2", "3c4"]
// 最终匹配到的所有结果

如果不使用循环方式连续获取匹配内容,即使是添加了全局匹配,每次执行也只会得到一组结果

5. 字符串与正则相关的方法

String.prototype.search()

使用语法:index = str.search(pattern)

用于检索字符串中指定的字符串,或检索与正则表达式相匹配的字符串。方法返回第一个匹配结果 index,查找不到返回 -1。

search 方法不执行全局匹配,会忽略修饰符 g,并且总是从字符串的开始位置进行检索

var str = '1a2b3c4d1a';
console.log(str.search('a'));   // 1
console.log(str.search('1'));   // 0
console.log(str.search(1));     // 0
console.log(str.search(/1/));   // 0
console.log(str.search(/1/g));  // 0
console.log(str.search(/f/));   // -1

对于 search 方法传递字符串或者数字都可以,参数会尝试转成正则进行检索。

String.prototype.match()

使用语法:str.match(pattern)

用于检索字符串,以找到一个或多个与 pattern 匹配的文本。pattern 是否具有修饰符 g 对结果影响很大。

非全局调用(不使用 g)

match 方法只能在字符串中执行一次匹配。如果没有找到任何匹配字符,返回 null;否则返回一个数组,其中存放了与它找到的匹配文本有关信息

  • 第一个元素存放的是匹配文本,其余的元素存放的是与正则表达式的子表达式(分组)匹配的文本
  • 除了常规的数组元素之外,返回的数组还含有 2 个对象属性
  • index 表示匹配文本的在字符串中的起始位置
  • input 表示对匹配字符串的引用
  • 和 exec 方法的返回值很像
var str = '$1a2b3c4d1a';
var pattern = /\d(\w)\d/;
var result = str.match(pattern);
console.log(result);              // ["1a2", "a", index: 1, input: "$1a2b3c4d1a", groups: undefined]
console.log(result.index);        // 1
console.log(pattern.lastIndex);   // 0

没有修饰符 g,正则表达式的 lastIndex 不起作用。

全局调用(使用 g)

match 方法将执行全局检索,找到字符串中的所有匹配子字符串

  • 没有找到任何匹配的子串,则返回 null
  • 如果找到了一个或多个匹配子串,则返回由子串组成的数组,这个数组没有 index 属性或者 input 属性
var str = '$1a2b3c4d1a';
var pattern = /\d(\w)\d/g;
var result = str.match(pattern);
console.log(result);              // ["1a2", "3c4"]
console.log(result.index);        // undefined
console.log(pattern.lastIndex);   // 0

String.prototype.split()

使用语法:str.split(pattern)

用于使用某些指定子串或正则表达式将字符串切割为数组。

var str1 = 'a.b.c.d';
var str2 = 'a1b2c3d4e';
console.log(str1.split('.'));   // ["a", "b", "c", "d"]
console.log(str2.split(/\d/));  // ["a", "b", "c", "d", "e"]

String.prototype.replace()

适用语法:str.replace(childStr, newStr) / str.replace(pattern, newStr)

用于将字符串中的某些子串替换成新的子串

当使用新字符串替换指定子串时,只会替换第一个

console.log('a1b'.replace('1', 2));     // 'a2b'
console.log('a1b1c1'.replace('1', 2));  // 'a2b1c1'

在需要进行多个替换时,则需使用正则表达式

console.log('a1b1c1'.replace(/1/g, 2)); // 'a2b2c2'

replace 方法的第二个参数还可以使用函数,这个函数会在每次替换时调用。函数可以至少有三个参数

  1. 匹配的字符串
  2. 正则表达式分组内容,没有分组则没有该参数
  3. 匹配项在字符串中的 index
  4. 原字符串

函数内部必须要有一个返回值,作为下一次替换的新内容。这种方法多适用于每次替换的都是不同的新内容。

// 替换目标
// 'a1b2c3d4e5' ===> 'a2b3c4d5e6'

var str = 'a1b2c3d4e5';
var pattern = /\d/g;

var result = str.replace(pattern, function (match, index, origin) {
    console.log(match);   // 1 2 3 4 5
    return parseInt(match) + 1
})

console.log(result);  // 'a2b3c4d5e6'
var str = 'a1b2c3d4e5';
var pattern = /(\w)(\d)(\w)/g;

var result = str.replace(pattern, function (match, group1, group2, group3, index, origin) {
    console.log('match', match);      // match a1b     match c3d
    console.log('group1', group1);    // 
    console.log('group2', group2);    // 
    console.log('group3', group3);    // 
    return group1 + group2 + group3;
})

console.log(result);    // 

// 依次输出结果
// match a1b
// group1 a
// group2 1
// group3 b
// match c3d
// group1 c
// group2 3
// group3 d
// a12c34e5

2. 使用场景

js中的正则表达式(1)

【正则表达式系列】零宽断言

JS 正则表达式否定匹配(正向前瞻)


评论:

温馨提示

  • 请确保输入的邮箱地址正确,以便及时收到回复邮件
  • 评论内容需要合规合法,任何不符合国家法律和不健康的内容将会被删除