ES5 正则表达式

1.两种语法

1
2
3
var patt = new RegExp('abc','g')
//或者
var regex = new RegExp(/xyz/i);

查找 abc,其中的 g 是修饰符,表示全局的
其中的 i 是修饰符,表示不区分大小写
注意:当使用构造函数创造正则对象时,需要常规的字符转义规则(在前面加反斜杠 \)。比如,以下是等价的:

1
var patt = /abc/g

意思和上边相同

2.三种方法

test() //返回 true 或者 false
exec() //返回的是数值,是数组,重复数值只显示一次
match() //返回的是数值,是数组,重复数值显示多次

exec 和 match 区别
var reg = /abc/g
var str = “1abc2,3abc4”
console.log(reg.exec(str)) //返回 abc 只匹配一个
console.log(str.match(reg)) //返回 abc,abc 匹配多个
reg.test(str)
补:
replace()
search()
split()

3.常用元字符

. 匹配除换行符以外的其他字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白字符
\d 匹配数字
\b 匹配单词的开始或结束
\n 匹配新行
\r 匹配行首
^ 匹配字符串的开始
\$ 匹配字符串的结束

备注:

1
2
如果文本不存在换行符,那么.和[\b\B]和[\d\D]和[\s\S]和[\w\W]是等价的。
如果文本存在换行符,那么(.|\n)和[\b\B]和[\d\D]和[\s\S]和[\w\W]是等价的。

4.字符转义

在前边加\ 一般.和*还有()都需要

5.限定符-重复

* 重复 0 次或更多次
+ 重复 1 次或更多次
? 重复 0 次或 1 次
{n} 重复 n 次
{n,} 重复 n 或更多次
{n,m}重复 n 到 m 次

6.字符类-查找字符集合

[] 匹配[]内的元素,字符之前是或的关系,返回也是一个字符而不是一个单词
例:
[.?!] 匹配.或?或!
[a-z] 匹配 a 到 z 之间的数任意一个字符
[A-Z] 匹配 A 到 Z
[0-9] 匹配 0 到 9

7.分支条件-类似于或的关系

| 两种规则可以通过|来实现或的关系

8.分组-就是上边的重复,只是对象不是单字符了

() 被括起来的进行分组
例:
(\d{1,3}){3} 表示一个 1 到 3 位的数字,重复三遍–就是三个三位数字

9.反义–和元字符意思相反

\W 匹配任意不是字母,数字,下划线,汉字
\S 匹配任意不是空白符的字符
\D 匹配任意不是数字的字符
\B 匹配任意不是单词的开始或结束位置
[\^x] 匹配任意除了 x 以外的任意字符
[\^aeiou] 匹配除了 aeiou 以外的任意字符

10 分组的两种形式

捕获组和非捕获组

10.1 捕获组

10.1.1 向后引用–取到匹配值返回数组

(exp) 和分组很像,都是用括号括起来,但是这里返回的是查到符合 exp 的数组,而分组不反回数组

6
1
2
3
4
5
'<App>hello regex</App><p>A</p><p>hello regex</p>'.match(/<((A|a)pp)>(hello regex)+<\/\1><p>\2<\/p><p>\3<\/p>/);
// ["<App>hello regex</App><p>A</p><p>hello regex</p>", "App", "A", "hello regex", index: 0, input: "<App>hello regex</App><p>A</p><p>hello regex</p>", groups: undefined]
// 如果有嵌套的圆括号,那么捕获的引用是先递归的,然后才是下一个顶级捕获。
// 所以是先APP 后 A
// 捕获使用 \数字 的形式,分别对应前面的圆括号捕获的内容。这种捕获的引用也叫反向引用。

10.1.2 捕获命名

(?exp) 和上边效果一样,只不过他返回到数组有名字,是 name

6
1
2
'<App>hello regex</App>'.match(/<(?<tag>[a-zA-Z]+)>.*<\/\k<tag>>/);
// ["<App>hello regex</App>", "App", index: 0, input: "<App>hello regex</App>", groups: {tag: "App"}]

在捕获组内部最前面加上?,它就被命名了。使用\k语法就可以引用已经命名的捕获组。

10.2 非捕获组

(?:exp) 捕获符合 exp 的元素,不捕获匹配的文本 会省一些系统资源

6
1
2
3
4
5
6
// 捕获组
'@abc'.match(/@(abc)/);
// ["@abc", "abc", index: 0, input: "@abc", groups: undefined]
// 非捕获
'@abc'.match(/@(?:abc)/);
// ["@abc", index: 0, input: "@abc", groups: undefined]

11.零宽断言–一般就是只查到这个词之后向前找或者向后找

零宽(zero-width)是什么意思?指的就是它匹配一个位置,本身没有宽度。
断言(assertion)是什么意思?指的是一种判断,断言之前或之后应该有什么或应该没有什么。

11.1 正向零宽断言

11.1.1 先行断言

(?=exp) 匹配到 exp 的元素,向他前边找要匹配的元素 例:\b\w+(?=ing\b) ing 前边要有字符,且一直向前找到头返回字符串

11.1.2 后行断言

(?<=exp) 匹配到 exp 后边的位置

11.2 负向零宽断言–和反义字符[^x]差不多是一个意思

11.2.1 先行断言

(?!exp) 匹配不包含 exp 规则的字符 例:\b\wq(?!u)\w\b 表示 q 后边是一个非 u 的字符
例子:

1
2
3
const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./', false, /^((?!index).)*\.js$/) // 匹配非index.js的js文件
const iconMap = requireAll(req)

正则表达式中有(?=a)和(?!a)来表示我们是否需要匹配(找到)某个东西。
而上边说的匹配和不匹配是说 match 出来的数组,是两个概念

1
2
3
^(?!.*hello)
其他
/^(?!.*u/zzfinance.*js)/ log://{eruda.js}

这里.*用来表示 hello 之前可能有其他的字符,为什么还要加^呢,因为如果不加的话,可能匹配到 h 之后的这个位置上了。

11.2.2 后行断言

(?<!exp) 匹配 exp 前边的位置 (?<![a-z])\d{7}返回七位数字,且他们前边不能是小写字母

12. 贪婪–匹配尽可能多的字符

a.*b 匹配字符串中最长的从 a 到 b 的字符串,可以理解为从第一个 a 开始找,一直找到最后一个 b 结束

13. 懒惰–匹配从第一个字符到最近的结束字符的字符串

*? 重复任意次,但尽可能少重复 例:a.*?b 原式:aabab 匹配到:aab ab
+? 重复 1 次或任意次,但尽可能少重复
?? 重复 n 次或更多次,但尽可能少重复
{n,m}?重复 n 次到 m 次,但尽可能少重复
{n,}? 重复 n 次或更多次,但尽可能少重复

14. 贪婪和懒惰对比讲解

6
1
2
3
4
5
6
// 贪婪-默认
'https'.match(/http(s)?/);
// ["https", "s", index: 0, input: "https", groups: undefined]
// 懒惰
'https'.match(/http(s)??/);
// ["http", undefined, index: 0, input: "https", groups: undefined]

紧跟在?后面的?它不是一个量词,而是一个模式切换符,从贪婪模式切换到非贪婪模式。
贪婪模式在正则中是默认的模式,就是在既定规则之下匹配尽可能多的文本。
因为正则中有量词,它的重复次数可能是一个区间,这就有了取舍。
紧跟在量词之后加上?就可以开启非贪婪模式。怎么省事怎么来。(上边的例子就是最少可以是 0 次)
这里的要点是,?必须紧跟着量词(所以不一定是?),否则的话它自己就变成量词了。

ES6 正则表达式

1 字符=2 字节或者 4 字节

1.RegExp 构造函数

1
2
var regex = new RegExp(/xyz/, i); //ES5不可以这么声明
new RegExp(/abc/gi, "i"); //取i而不是ig

2.字符串方法:

match()、replace()、search()和 split()
在语言内部全部调用 RegExp 的实例方法,从而做到所有与正则相关的方法,全都定义在 RegExp 对象上。

3.u 修饰符

含义为“Unicode 模式”,用来正确处理大于\uFFFF 的 Unicode 字符。

1
2
/^\uD83D/u.test("\uD83D\uDC2A");
// false 加上u后,识别后边的'\uD83D\uDC2A'为一个字符不能拆开,前边匹配的只是它的一部分,所以是false

3.1 点字符

点.字符是除了换行符以外的任意单个字符。对于码点大于 0xFFFF 的 Unicode 字符,点字符不能识别,必须加上 u 修饰符。

1
2
3
4
var s = '𠮷';

/^.$/.test(s) // false 无法识别4字节编码字符
/^.$/u.test(s) // true 只是针对识别4个字节的编码

3.2 Unicode 字符表示法

使用大括号表示 Unicode 字符,u 修饰符(/后边的那个),才能识别。

1
2
3
4
5
/\u{61}/.test("a") / // false    第一个\u仅仅是字符编码的固定格式,没有第二个u的话,这个式子就不是识别4字节的方法,而是普通的两字节
a /
u.test("a") / // true
𠮷 /
u.test("𠮷"); // true

3.3 量词

使用 u 修饰符后,所有量词都会正确识别大于码点大于 0xFFFF 的 Unicode 字符。

1
2
/𠮷{2}/.test('𠮷𠮷') // false   没有u,还无法识别前边那个𠮷
/𠮷{2}/u.test('𠮷𠮷') // true

3.4 预定义模式

1
2
/^\S$/.test('𠮷') // false  上面代码的\S是预定义模式,匹配所有不是空格的字符,无法识别𠮷
/^\S$/u.test('𠮷') // true 上面代码的\S是预定义模式,匹配所有不是空格的字符

3.5 i 修饰符

1
2
3
4
5
//\u004B与\u212A都是大写的K,证明\u212A是非规范的

/[a-z]/i.test("\u212A") / // false
[a - z] /
iu.test("\u212A"); // true 只有加u才能识别出来

4.y 修饰符

除了 u 修饰符,ES6 还为正则表达式添加了 y 修饰符,叫做“粘连”(sticky)修饰符。
y 修饰符的作用与 g 修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g 修饰符只要剩余位置中存在匹配就可,而 y 修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。

1
2
3
4
5
6
7
8
9
var s = "aaa_aa_a";
var r1 = /a+/g;
var r2 = /a+/y;
//第一次执行,exec只能取一次
r1.exec(s); // ["aaa"]
r2.exec(s); // ["aaa"]
//第二次执行
r1.exec(s); // ["aa"]
r2.exec(s); // null //紧连的是一个`—`所以为null

使用lastIndex属性,可以更好地说明 y 修饰符,不包含位置字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const REGEX = /a/y;

// 指定从2号位置开始匹配
REGEX.lastIndex = 2;

// 不是粘连,匹配失败
REGEX.exec("xaya"); // null

// 指定从3号位置开始匹配
REGEX.lastIndex = 3;

// 3号位置是粘连,匹配成功
const match = REGEX.exec("xaxa");
match.index; // 3
REGEX.lastIndex; // 4

y 修饰符号隐含了头部匹配的标志 ˆ
在 split 方法里,如果匹配成功意味着第一个成员肯定 split 括号内的参数,参数被替换后变成都逗号,逗号前边就肯定是个空字符了

1
2
3
4
5
6
7
// 没有找到匹配
"x##".split(/#/y);
// [ 'x##' ]

// 找到两个匹配
"##x".split(/#/y);
// [ '', '', 'x' ]

用途:可以判断是否含有非法字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y;
const TOKEN_G = /\s*(\+|[0-9]+)\s*/g;

tokenize(TOKEN_Y, "3 + 4");
// [ '3', '+', '4' ]
tokenize(TOKEN_G, "3 + 4");
// [ '3', '+', '4' ]

function tokenize(TOKEN_REGEX, str) {
let result = [];
let match;
while ((match = TOKEN_REGEX.exec(str))) {
result.push(match[1]);
}
return result;
}

5.sticky 属性

判断是否设置了 y 修饰符。

1
2
var r = /hello\d/y;
r.sticky; // true

6.flags 属性

返回正则表达式的修饰符。

1
2
3
4
5
6
7
8
9
10
// ES5的source属性
// 返回正则表达式的正文
/abc/gi.source /
// "abc"

// ES6的flags属性
// 返回正则表达式的修饰符
abc /
ig.flags;
// 'gi'

7.RegExp.escape()

字符串必须转义,才能作为正则模式。
未放入 ES7,所以暂时不讨论

8.后行断言

先行断言指的是,x 只有在 y 前面才匹配,必须写成/x(?=y)/
比如,只匹配百分号之前的数字,要写成/\d+(?=%)/

先行否定断言指的是,x 只有不在 y 前面才匹配,必须写成/x(?!y)/
比如,只匹配不在百分号之前的数字,要写成/\d+(?!%)/。

后行断言正好与”先行断言”相反,x 只有在 y 后面才匹配,必须写成/(?<=y)x/
比如,只匹配美元符号之后的数字,要写成/(?<=\$)\d+/

后行否定断言则与”先行否定断言”相反,x 只有不在 y 后面才匹配,必须写成/(?<!y)x/
比如,只匹配不在美元符号后面的数字,要写成/(?<!\$)\d+/。

参考文献:http://www.jb51.net/tools/zhengze.html
参考文献:https://www.jb51.net/article/52491.htm
参考文献:http://es6.ruanyifeng.com/#docs/regex
一次性搞懂 JavaScript 正则表达式之语法

Whistle 常用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/https:\/\/app.zhuanzhuan.com\/zz\/transfer\/zzfinance\?([^=&]+)=([^&]*)/ http://localhost:3000/$2 resDelay://0000

/^(?!.*u/zzfinance.*js)/ log://{eruda.js}
/^((?!list|news|index).)*$/.test('list0')
/^https:\/\/m\.zhuanzhuan\.com\/u\/zzfinance\/((?!\.).)*$/
/^https:\/\/m\.zhuanzhuan\.com\/u\/zzfinance\/[^.]*$/ log://{eruda.js}
/m\.zhuanzhuan\.com((?!.(js|png|jpg|gif|jpeg)).)*$/ log://{eruda.js}

app.zhuanzhuan.com zapi.zhuanspirit.com/mock/179
/getHomePageData/ resCors://{resCORS} # // 开启跨域允许get,post请求
/getHomePageData/ xtpl://{mock3.json}
^和$必须加
https://m.zhuanzhuan.com/u/zzfinance/Mine/Home
https://m.zhuanzhuan.com/u/zzfinance/9.js
← Prev Next →