【JS】128-重温基础:正则表达式

脑洞前端

共 7981字,需浏览 16分钟

 ·

2020-08-15 19:42


本文是 「重温基础」 系列文章的第九篇。
今日感受:时间管理-角色管理法。

「本章节复习的是JS中的正则表达式,JS中用来匹配字符串的强大工具。」

前置知识:
JS中的正则表达式是用来匹配字符串中指定字符组合的模式。
另外需要记住:正则表达式也是对象。

1.创建正则表达式

  • 使用一个正则表达式字面量:
let reg = /ab+c/;
let reg = /^[a-zA-z]/gi;
  • 使用RegExp对象:
    new RegExp(str[, attr])接收2个参数,str是一个字符串,指定正则表达式匹配规则,attr可选,表示匹配模式,值有g(全局匹配),i(区分大小写的匹配)和m(多行匹配)。
let reg = new RegExp('ab+c');
let reg = new RegExp(/^[a-zA-z]/"gi");
let reg = new RegExp("^[a-zA-z]""gi");

正则表达式的返回值,是一个新的RegExp对象,具有指定的模式和标志。
「返回信息介绍」

对象属性描述案例中对应的值
reglastIndex下一个匹配的索引(仅在使用g参数时可用)0
regsource模式文本。在正则表达式创建时更新,不执行。"ab+c"
regignoreCase是否使用了 "i" 标记使正则匹配忽略大小写。true
regglobal是否使用了 "g" 标记来进行全局的匹配。true
regmultiline是否使用了 "m" 标记使正则工作在多行模式。false

「关于正则表达式的一些方法属性,文章后面介绍,这里先复习定义和使用」

2.使用正则表达式

JS的正则表达式可以被用于:

  • RegExp对象的exectest方法;
  • String对象的matchreplacesearchsplit方法。

2.1 RegExp对象方法

方法介绍
exec检索字符串中指定的值。返回找到的值,并确定其位置。
test检索字符串中指定的值。返回 truefalse

2.1.1 exec(str)

str: 需要检索的字符串。
若检索成功,返回匹配的数组,否则返回null。

let str = "hello leo!";
let reg = new RegExp("leo""g");
let result = reg.exec(str);
// 也可以写成
let result = /leo/g.exec("hello leo!");

/*
[
    0: "leo",
    groups: undefined,
    index: 6,
    input: "hello leo!",
    length: 1
]
*/


let result2 = /(leo \S)/g.exec("hello leo hi leo!");
/*
    0: "leo hi"
    1: "leo hi"
    2: "hi"
    groups: undefined
    index: 6
    input: "hello leo hi leo!"
    length: 3
*/

「返回信息介绍」

对象属性描述案例中对应的值
result[0]匹配到的所有字符串"leo"
resultinput初始字符串。"hello leo!"
resultindex在输入的字符串中匹配到的以0开始的索引值。6
result2[1],...,[n]括号中的分组捕获[1]=> "leo hi";[2] => "hi"

2.1.2 test(str)

str:需要检索的字符串。
若匹配成功返回true否则false
等价于 reg.exec(str) != null

let str = "hello leo!";
let res = /^leo/.test(str);   // fasle

^str表示匹配以str开头的字符串,这些符号文章后面会介绍。

「注意:」

若正则使用全局标志( g ),则test()会改变正则表达式的 lastIndex 属性,连续调用test()方法,后续的执行将会从 lastIndex 处开始匹配字符串,(exec() 同样改变正则本身的 lastIndex属性值):

var regex = /leo/g;

// regex.lastIndex is at 0
regex.test('leo'); // true

// regex.lastIndex is now at 3
regex.test('leo'); // false

2.2 String对象方法

方法介绍
search检索与正则表达式相匹配的值。
match找到一个或多个正则表达式的匹配。
replace替换与正则表达式匹配的子串。
split把字符串分割为字符串数组。

2.2.1 search

str.search(reg)
str:被检索的源字符串。
reg:可以是需要检索的「字符串」,也可以是需要检索的RegExp对象,可以添加标志,如i

若检索成功,返回「第一个」RegExp对象匹配的字符串的起始位置,否则返回-1

let str = "hello leo!";
let res = str.search(/leo/g);  // 6

let str1 = "hello leoleoleoleo!";
let res1 = str.search(/leo/g); // 6
let res2 = str.search(/pingan/g); // -1

2.2.2 match

str.match(reg)
str:被检索的源字符串。
reg:可以是需要检索的「字符串」,也可以是需要检索的RegExp对象,可以添加标志,如i

若检索成功,返回与reg匹配的所有结果的一个「数组」,数组的第一项是进行匹配完整的字符串,之后的项是用圆括号捕获的结果,否则返回null

let str = 'For more information, see Chapter 3.4.5.1';
let reg = /see (chapter \d+(\.\d)*)/i;
let result = str.match(reg);
/*
logs [ 'see Chapter 3.4.5.1',
       'Chapter 3.4.5.1',
       '.1',
       index: 22,
       input: 'For more information, see Chapter 3.4.5.1' ]
*/

'see Chapter 3.4.5.1' 是整个匹配。
'Chapter 3.4.5.1''(chapter \d+(\.\d)*)'捕获。
'.1' 是被'(\.\d)'捕获的最后一个值。
'index' 属性(22)是整个匹配从零开始的索引。
'input' 属性是被解析的原始字符串。

2.2.3 replace

将字符串中「指定字符」替换成「其他字符」,或替换成一个与正则表达式匹配的字符串。
str.replace(sub/reg,val):

  • str: 源字符串
  • sub: 使用字符串来检索被替换的文本
  • reg: 使用RegExp对象来检索来检索被替换的文本
  • val: 指定替换文本
    返回替换成功之后的字符串,不改变源字符串内容。
let str = "hello leo!";
let res = str.replace('leo','pingan');//"hello pingan!"

「val可以使用特殊变量名」

变量名代表的值
$$插入一个 "$"。
$&插入匹配的子串。
$插入当前匹配的子串左边的内容。
$'插入当前匹配的子串右边的内容。
$n假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始
let str = "hello leo!";
let res = str.replace(/(\w+)\s* \s*(\w+)/"$2:$1");
// "leo:hello!"

2.2.4 split

将一个字符串,按照指定符号分割成一个字符串数组。
str.split(sub[, maxlength]):

  • str: 源字符串
  • sub: 指定的分割符号或正则
  • maxlength: 源字符串
let str = "hello leo!";
let res = str.split();   //["hello leo!"]
let res = str.split(""); //["h", "e", "l", "l", "o", " ", "l", "e", "o", "!"]
let res = str.split(" ");//["hello", "leo!"]
let res = str.split(/\s+/);//["hello", "leo!"]

let res = str.split("",3);//["h", "e", "l"]

2.3 使用情况

  • 当我们想要查找一个字符串中的一个匹配「是否找到」,可以用testsearch方法。
  • 当我们想要得到匹配的「更多信息」,我们就需要用到execmatch方法。

3.正则表达式符号介绍

详细的每个符号的用法,可以查阅 W3school JavaScript RegExp 对象

3.1 修饰符

修饰符描述
i执行对大小写不敏感的匹配。
g执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m执行多行匹配。
let str = "hello leo!"
let res = /Leo/i.test(str); // i 不区分大小写 所以返回true
let res = /Leo/.test(str);  // fasle

3.2 方括号

用于查找指定返回之内的字符:

表达式描述
[abc]查找方括号之间的任何字符。
[^abc]查找任何不在方括号之间的字符。
[0-9]查找任何从 0 至 9 的数字。
[a-z]查找任何从小写 a 到小写 z 的字符。
[A-Z]查找任何从大写 A 到大写 Z 的字符。
[A-z]查找任何从大写 A 到小写 z 的字符。
[adgk]查找给定集合内的任何字符。
[^adgk]查找给定集合外的任何字符。
(red)查找任何指定的选项。
let str = "hello leo!";
let res = str.match(/[a-m]/g);
//["h", "e", "l", "l", "l", "e"]
let res = str.match(/[^a-m]/g);
//["o", " ", "o", "!"]

3.3 元字符

元字符是拥有特殊含义的字符:

元字符描述
.查找单个字符,除了换行和行结束符。
\w查找单词字符。
\W查找非单词字符。
\d查找数字。
\D查找非数字字符。
\s查找空白字符。
\S查找非空白字符。
\b匹配单词边界。
\B匹配非单词边界。
\0查找 NUL 字符。
\n查找换行符。
\f查找换页符。
\r查找回车符。
\t查找制表符。
\v查找垂直制表符。
\xxx查找以八进制数 xxx 规定的字符。
\xdd查找以十六进制数 dd 规定的字符。
\uxxxx查找以十六进制数 xxxx 规定的 Unicode 字符。
let str = "hello leo hi pingan!";
let res = str.match(/o\s/g);
//["o ", "o "]
let res = str.match(/\s/g);
//[" ", " ", " "]

3.4 量词

量词描述
n+匹配任何包含至少一个 n 的字符串。
n*匹配任何包含零个或多个 n 的字符串。
n?匹配任何包含零个或一个 n 的字符串。
n{X}匹配包含 X 个 n 的序列的字符串。
n{X,Y}匹配包含 X 至 Y 个 n 的序列的字符串。
n{X,}匹配包含至少 X 个 n 的序列的字符串。
n$匹配任何结尾为 n 的字符串。
^n匹配任何开头为 n 的字符串。
?=n匹配任何其后紧接指定字符串 n 的字符串。
?!n匹配任何其后没有紧接指定字符串 n 的字符串。
let str = "hello leo!";
let res = str.match(/^hello/g);
// ["hello"]
let res = str.match(/^pingan/g);
//null

4. 正则表达式拓展(ES6)

4.1 介绍

在ES5中有两种情况。

  • 参数是「字符串」,则第二个参数为正则表达式的修饰符。
let a = new RegExp('abc''i');
// 等价于
let a = /abx/i;
  • 参数是「正则表达式」,返回一个原表达式的拷贝,且不能有第二个参数,否则报错。
let a = new RegExp(/abc/i);
//等价于
let a = /abx/i;

let a = new RegExp(/abc/'i');
//  Uncaught TypeError

ES6中使用:
第一个参数是正则对象,第二个是指定修饰符,如果第一个参数已经有修饰符,则会被第二个参数覆盖。

new RegExp(/abc/ig'i');

4.2 字符串的正则方法

常用的四种方法:match()replace()search()split()

4.3 u修饰符

添加u修饰符,是为了处理大于uFFFF的Unicode字符,即正确处理四个字节的UTF-16编码。

/^\uD83D/u.test('\uD83D\uDC2A'); // false
/^\uD83D/.test('\uD83D\uDC2A');  // true

由于ES5之前不支持四个字节UTF-16编码,会识别为两个字符,导致第二行输出true,加入u修饰符后ES6就会识别为一个字符,所以输出false

「注意:」
加上u修饰符后,会改变下面正则表达式的行为:

  • (1)点字符 点字符(.)在正则中表示除了「换行符」以外的任意单个字符。对于码点大于0xFFFF的Unicode字符,点字符不能识别,必须加上u修饰符。
var a = "?";
/^.$/.test(a);  // false
/^.$/u.test(a); // true
  • (2)Unicode字符表示法 使用ES6新增的大括号表示Unicode字符时,必须在表达式添加u修饰符,才能识别大括号。
/\u{61}/.test('a');      // false
/\u{61}/u.test('a');     // true
/\u{20BB7}/u.test('?'); // true
  • (3)量词 使用u修饰符后,所有量词都会正确识别码点大于0xFFFF的 Unicode 字符。
/a{2}/.test('aa');    // true
/a{2}/u.test('aa');   // true
/?{2}/.test('??');  // false
/?{2}/u.test('??'); // true
  • (4)i修饰符 不加u修饰符,就无法识别非规范的K字符。
/[a-z]/i.test('\u212A'// false
/[a-z]/iu.test('\u212A'// true

「检查是否设置u修饰符:」使用unicode属性。

const a = /hello/;
const b = /hello/u;

a.unicode // false
b.unicode // true

4.4 y修饰符

y修饰符与g修饰符类似,也是全局匹配,后一次匹配都是从上一次匹配成功的下一个位置开始。区别在于,g修饰符「只要」剩余位置中存在匹配即可,而y修饰符是必须从「剩余第一个」开始。

var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;

r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]

r1.exec(s) // ["aa"]  剩余 '_aa_a'
r2.exec(s) // null

lastIndex属性」: 指定匹配的开始位置:

const a = /a/y;
a.lastIndex = 2;  // 从2号位置开始匹配
a.exec('wahaha'); // null
a.lastIndex = 3;  // 从3号位置开始匹配
let c = a.exec('wahaha');
c.index;          // 3
a.lastIndex;      // 4

「返回多个匹配」
一个y修饰符对match方法只能返回第一个匹配,与g修饰符搭配能返回所有匹配。

'a1a2a3'.match(/a\d/y);  // ["a1"]
'a1a2a3'.match(/a\d/gy); // ["a1", "a2", "a3"]

「检查是否使用y修饰符」
使用sticky属性检查。

const a = /hello\d/y;
a.sticky;     // true

4.5 flags属性

flags属性返回所有正则表达式的修饰符。

/abc/ig.flags;  // 'gi'

5. 正则表达式拓展(ES9)

在正则表达式中,点(.)可以表示任意单个字符,除了两个:用u修饰符解决「四个字节的UTF-16字符」,另一个是行终止符。
「终止符」即表示一行的结束,如下四个字符属于“行终止符”:

  • U+000A 换行符(\n)
  • U+000D 回车符(\r)
  • U+2028 行分隔符(line separator)
  • U+2029 段分隔符(paragraph separator)
/foo.bar/.test('foo\nbar')
// false

上面代码中,因为.不匹配\n,所以正则表达式返回false
换个醒,可以匹配任意单个字符:

/foo[^]bar/.test('foo\nbar')
// true

ES9引入s修饰符,使得.可以匹配任意单个字符:

/foo.bar/s.test('foo\nbar'// true

这被称为dotAll模式,即点(dot)代表一切字符。所以,正则表达式还引入了一个dotAll属性,返回一个布尔值,表示该正则表达式是否处在dotAll模式。

const re = /foo.bar/s;
// 另一种写法
// const re = new RegExp('foo.bar', 's');

re.test('foo\nbar'// true
re.dotAll // true
re.flags // 's'

/s修饰符和多行修饰符/m不冲突,两者一起使用的情况下,.匹配所有字符,而^$匹配每一行的行首和行尾。

参考资料

1.MDN 正则表达式
2.W3school JavaScript RegExp 对象

推荐阅读


1、你不知道的前端异常处理(万字长文,建议收藏)

2、你不知道的 TypeScript 泛型(万字长文,建议收藏)

3、Webkit 内核初探

4、immutablejs 是如何优化我们的代码的?

5、连肝7个晚上,总结了计算机网络的知识点!(共66条)

6、想去力扣当前端,TypeScript 需要掌握到什么程度?

7、【校招面经分享】好未来-北京-视频面试


关注加加,星标加加~


如果觉得文章不错,帮忙点个在看呗






浏览 39
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报