正则表达式迷你书
1. 第一章 正则表达式字符匹配攻略
1.1. 两种模糊匹配
正则表达式之所以强大,是因为其能实现模糊匹配。而模糊匹配,有两个方向上的“模糊”:横向模糊和纵向模糊
1.1.1. 横向模糊匹配
横向模糊指的是,一个正则可匹配的字符串的长度不是固定的,可以是多种情况的。
其实现的方式是使用量词。譬如 {m,n},表示连续出现最少 m 次,最多 n 次
var regex = /ab{2,5}c/g;
var string = "abc abbc abbbc abbbbc abbbbbc abbbbbbc";
console.log( string.match(regex) );
// => ["abbc", "abbbc", "abbbbc", "abbbbbc"]
2
3
4
1.1.2. 纵向模糊匹配
纵向模糊指的是,一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种 可能。
其实现的方式是使用字符组。譬如 [abc],表示该字符是可以字符 "a"、"b"、"c" 中的任何一个。
var regex = /a[123]b/g;
var string = "a0b a1b a2b a3b a4b";
console.log( string.match(regex) );
// => ["a1b", "a2b", "a3b"]
2
3
4
1.2. 字符组
需要强调的是,虽叫字符组(字符类),但只是其中一个字符。
1.2.1. 范围表示法
字符组里的字符特别多的话,怎么办?可以使用范围表示法。比如 [123456abcdefGHIJKLM],可以写成 [1-6a-fG-M]。用连字符 - 来省略和简写。
单独使用-z字符就需要避免识别成范围表示法,所以可以写成如下的方式:[-az] 或 [az-] 或 [a-z]
1.2.2. 排除字符组
纵向模糊匹配,还有一种情形就是,某位字符可以是任何东西,但就不能是 "a"、"b"、"c"。 ^(脱字符),表示求反的概念, 也有相应的范围表示法。
1.2.3. 常见的简写形式
如果要匹配任意字符怎么办?可以使用 [\d\D]、[\w\W]、[\s\S] 和 [^] 中任何的一个。
1.3. 量词
量词也称重复。掌握 {m,n} 的准确含义后,只需要记住一些简写形式
1.3.1. 简写形式
1.3.2. 贪婪匹配与惰性匹配
贪婪匹配 但是其是贪婪的,它会尽可能多的匹配。你能给我 6 个,我就要 5 个。你能给我 3 个,我就要 3 个。 反正只要在能力范围内,越多越好。
var regex = /\d{2,5}/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex) );
// => ["123", "1234", "12345", "12345"]
2
3
4
惰性匹配
通过在量词后面加个问号就能实现惰性匹配,因此所有惰性匹配情形如下:{m,n}?、{m,}?、{,n}?
其中 /\d{2,5}?/ 表示,虽然 2 到 5 次都行,当 2 个就够的时候,就不再往下尝试了。
var regex = /\d{2,5}?/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex) );
// => ["12", "12", "34", "12", "34", "12", "34", "56"]
2
3
4
1.4. 多选分支
一个模式可以实现横向和纵向模糊匹配。而多选分支可以支持多个子模式任选其一 具体形式如下:(p1|p2|p3),其中 p1、p2 和 p3 是子模式,用 |(管道符)分隔,表示其中任何之一。
分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。
var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["good"]
2
3
4
1.5. 案例分析
匹配字符,无非就是字符组、量词和分支结构的组合使用罢了。
1.5.1. 匹配 16 进制颜色值
// 要求匹配:
// #ffbbad
// #Fc01DF
// #FFF
// #ffE
const regex = /^#[0-9a-fA-F]{6 | 3}/g;
其中字符可以出现 3 或 6 次,需要是用量词和分支结构。
var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
var string = "#ffbbad #Fc01DF #FFF #ffE";
console.log( string.match(regex) );
// => ["#ffbbad", "#Fc01DF", "#FFF", "#ffE"]
2
3
4
5
6
7
8
9
10
11
12
13
1.5.2. 匹配时间
// 以 24 小时制为例。
// 23:59
// 02:07
const regex = /^([01]\d|2[0-3]):([0-5]\d)$/g;
console.log( regex.test("23:59") );
console.log( regex.test("02:07") );
// => true
// => true
// 如果也要求匹配 "7:9"
const regex1 = /^(0?\d|1\d|2[0-3]):(0?\d|[1-5]\d)$/g;
2
3
4
5
6
7
8
9
10
11
12
13
1.5.3. 匹配日期
// 要求匹配
// 2017-06-10
const regex = /^[\d]{4}-(0[\d]|1[0-2])-(0[\d]|[1-2][\d]|3[0-1])$/g;
console.log(regex.test("2017-06-10"));
2
3
4
5
1.5.4. 匹配 id
// 要求匹配 id="container"
<div id="container" class="main"></div>
var regex = /id="[^"]*"/
var string = '<div id="container" class="main"></div>';
console.log(string.match(regex)[0]);
// => id="container"
2
3
4
5
6
7
8
2. 第二章 正则表达式位置匹配攻略
2.1. 什么是位置呢?
位置(锚)是相邻字符之间的位置
2.2. 如何匹配位置呢?
es5中,共有6个锚, ^、$、\b、\B、(?=p)、(?!p)
2.2.1. ^ 和 $
输入边界断言:^、$ 断言当前位置是输入的开始或结束,如果设置了 m 标志,则断言当前位置是一行的开始或结束。
2.2.2. \b 和 \B
单词边界断言:\b、\B
\b 是单词边界,具体就是 \w 与 \W 之间的位置,也包括 \w 与 ^ 之间的位置,和 \w 与 $ 之间的位置。 \B 就是 \b 的反面的意思,非单词边界。例如在字符串中所有位置中,扣掉 \b,剩下的都是 \B 的。 具体说来就是 \w 与 \w、 \W 与 \W、^ 与 \W,\W 与 $ 之间的位置。
2.2.3. (?=p) 和 (?!p)
(?=p),其中 p 是一个子模式,即 p 前面的位置,或者说,该位置后面的字符要匹配 p。
var result = "hello".replace(/(?=l)/g, '#');
console.log(result);
// => "he#l#lo"
2
3
而 (?!p) 就是 (?=p) 的反面意思,比如:
var result = "hello".replace(/(?!l)/g, '#');
console.log(result);
// => "#h#ell#o#"
2
3
ES5 之后的版本,会支持 positive lookbehind 和 negative lookbehind。 具体是 (?<=p) 和 (?<!p)。
2.3. 位置的特性
对于位置的理解,我们可以理解成空字符 ""。
"hello" == "" + "h" + "" + "e" + "" + "l" + "" + "l" + "" + "o" + "";
2.4. 相关案例
2.4.1. 不匹配任何东西的正则
让你写个正则不匹配任何东西
/.^/
2.4.2 数字的千位分隔符表示法
比如把 "12345678",变成 "12,345,678"。可见是需要把相应的位置替换成 ","。
// 1. 使用 (?=\d{3}$) 弄出最后一个逗号
var result = "12345678".replace(/(?=\d{3}$)/g, ',')
console.log(result);
// => "12345,678"
// 2.弄出所有的逗号
//逗号出现的位置,要求后面3个数字一组,也就是 \d{3} 至少出现一次
var result = "12345678".replace(/(?=(\d{3})+$)/g, ',')
console.log(result);
// => "12,345,678"
var result = "123456789".replace(/(?=(\d{3})+$)/g, ',')
console.log(result);
// => ",123,456,789"
// 因为上面的正则,仅仅表示把从结尾向前数,一但是 3 的倍数,就把其前面的位置替换成逗号。因此才会出
// 现这个问题。
// 怎么解决呢?我们要求匹配的到这个位置不能是开头。
// 我们知道匹配开头可以使用 ^,但要求这个位置不是开头怎么办? (?!^)
// 如果要把 "12345678 123456789" 替换成 "12,345,678 123,456,789"。
// 修改正则,把里面的开头 ^ 和结尾 $,修改成 \b
var string = "12345678 123456789",
regex = /(?!\b)(?=(\d{3})+\b)/g;
var result = string.replace(regex, ',')
console.log(result);
// => "12,345,678 123,456,789"
// 其中 (?!\b) 怎么理解呢?
// 要求当前是一个位置,但不是 \b 前面的位置,其实 (?!\b) 说的就是\B。 /\B(?=(\d{3})+\b)/g
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32