正则表达式

正则是用来对字符串进行查找或者处理的

正则表达式是匹配模式(match pattern): 要么匹配字符, 要么匹配位置

正则表达式字符匹配攻略

主要内容:

在量词后面加上 ? 表示惰性匹配(亦称作惰性量词)

  1. 两种模糊匹配
  2. 字符组
  3. 量词(+ 惰性匹配)
  4. 分支结构
  5. 案例分析

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"]

其中, g 是正则的修饰符,表示全局匹配,即在目标字符串中按顺序找到满足匹配模式的所有有子串,强调的是 “所有” 而不是 “第一个”

注意下 g 和 贪婪匹配没有关系

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. 字符组

虽然叫字符组(字符类),但只是其中的一个字符。例如: [abc], 表示匹配一个字符,他可以是 字符’a’, ‘b’, ‘c’ 之一。

2.1 范围表示法

使用连字符 - 来省略和简写:

比如: [123456abcdefGHIJKLM] 可以简写为:[1-6a-fG-M]

因为 连字符有特殊用途,那么匹配 ‘a’, ‘-‘, ‘z’ 三者中的任意一个字符: [-az]或者[az-]或者[a\-z] 即要么放在开头要么放在结尾,或者转义,只要不让 引擎认为表示范围就好了

2.2 排除字符组

纵向模糊匹配的时候,有一种情形是: 某位字符上表示任何除了 a,b,c 字符外的任何字符

此时就是 排除字符组(反义字符组)的概念。 如: [^abc] 表示 一个除 ‘a’ , ‘b’ , ‘c’ 之外的任意一个字符。 字符组的第一位 ^ (脱字符) ,有取反 之意

当然,也有相应的范围表示法

2.3 常见的简写形式

\d 就是 [0-9] ,表示一位数字。对应英文 digit(数字)

\D 就是 [^0-9], 表示除数字外的任意字符

\w 就是 [0-9a-zA-Z_] 或者 [0-9A-z](注意 A-z ASCII 码) ,表示 数字,字母,下划线。 word (单词),单词字符

注意 \w 不包含 $ 符号

\W 就是 [^0-9a-zA-Z] , 表示非单词字符

\s 就是 [\t\v\n\r\f] , 表空白符(号),包括 空格, 水平制表符,垂直制表符,换行符,回车符,换页符。 对应英文: space character

\S 就是 [^\t\v\r\n\f], 表示非空白符号

. 就是 [^\n\r\u2028\u2029] 。 通配符,表示几乎任意字符,除了: 换行符,回车符,行分隔符和段分隔符

如果要匹配任意字符, 可以使用 [\d\D] 、[\w\W]、[\s\S] 和 [^] 中任何一个

3. 量词

量词也称作重复, 掌握 {m,n}精准含义后,只需要记住一些简写形式。

3.1 简写形式

{m,}: 至少出现 m 次

{,n}: 至多出现 n 次

{m}: 出现 m 次

? : 等价于 {0,1} ,表示出现或者不出现

  • : 等价于 {1,} 表示至少出现 1 次

*: 等价于 {0,} 表示出现任意次, 有可能不出现

3.2 贪婪匹配 和 惰性匹配

看如下例子:

var regex = /\d{2,5}/g;
var string = '123 1234 12345 123456';
console.log(string.match(regex));
// =>["123", "1234", "12345", "12345"]

正则 /\d{2,5}/ 表示数字连续出现 2 到 5 次,可以匹配到 2,3,4 位连续数字

但是其是贪婪的,会尽可能多的匹配。能力范围内,越多越好

而惰性匹配,就是尽可能少的匹配:

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"]

其中 /\d{2,5}/表示, 2到5次都行,当2个就够的时候,就不再往下尝试了

通过在量词后面加个问号既可以实现惰性匹配,因此所有惰性匹配的情形如下:

一般只有量词才会涉及到贪婪和惰性

  1. {m,n}?
  2. {m,}?
  3. ??
  4. +?
  5. *?

惰性匹配的记忆方式: 量词后面加问号,问一句你知足了吗? 你很贪婪吗?

4. 多选分支

一个模式可以实现 横向和纵向的模糊匹配。而多选分支可以支持多个子模式任选其一。

具体形式: (p1| p2| p3) ,其中 p1 、p2 、p3 是子模式,用 | (管道符)分隔,表示其中任何之一。

var regex = /good|nice/g;
var string = "good idea , nice try";
console.log(string.match(regex));
 // ["good", "nice"]

分支结构的匹配是 惰性匹配,即当前面的匹配上了,后面就不再尝试了

var regex = /good|goodbye/g;
var string = 'goodbye';
console.log(string.match(regex));
// => ["good"]
var regex = /goodbye|good/g;
var string = 'goodbye';
console.log(string.match(regex));
// => ["goodbye"]

5. 案例分析

匹配字符,无非就是 字符组、量词和分支机构的组合使用罢了

5.1 匹配16进制颜色值

建议在 [] 不要使用 \d 使用 0-9

注意在 正则表达式中不要随意加 空格,空格也是会匹配的

// 要求匹配: #ffbbad #Fc01Df #fff #ffE

// var regex = /#[0-9a-fA-F]{6} | #[0-9a-fA-F]{3}/g
var regex = /#[\da-fA-F]{6} | #[\da-fA-F]{3}/g;
var string = '#ffbbad #Fc01Df #fff #ffE';
console.log(string.match(regex));
// >  ["#ffbbad", "#Fc01Df", "#fff", "#ffE"]
5.2 匹配时间

以24小时制为例。

要求匹配: 23:59 , 02:07

// 1. 共四位数, 第1位 可以是[0-2]
// 2. 当第一位 为2 时候, 第二位可以 [0-3],其他情况 第二位[0-9]
// 3. 第3 位 数字为 [0-5] 
// 4. 第4位 为 [0-9]
var regex = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/;
console.log(regex.test('23:59'));
console.log(regex.test('02:07'));
// > true
// > true

零可以省略情况:

// 扩大范围了
// var regex = /^([01]?[0-9]|[2][0-3]):[0-5]?[0-9]$/;
// var regex = /^(0?[0-9]|1[0-9]|[2][0-3]):((0?|[1-5])[0-9])$/;
// var regex = /^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/;
console.log(regex.test('23:59'));
console.log(regex.test('02:07'));
console.log(regex.test('2:7'));
// > true
// > true
// > true
5.3 匹配日期

比如: yyyy-mm-dd 格式为例

要求匹配:

分析:

  1. 年, 四位数字即可,可用 [0-9]{4}
  2. 月, 共12个月,分两种情况: 01、02…、09 和 10 、11、12, 可用 (0[1-9]|1[0-2] )
  3. 日,最大31 天,分为三种情况: 01、02、…、 09 和 10… 和 30、31,可用(0[1-9]|[12][0-9]|[3][01])
var regex = /^[0-9]{4}-0[1-9]|1[0-2]-0[1-9]|[12][0-9]|[3][01]$/
console.log(regex.test('2017-06-07'));
// true
5.4 window 操作系统文件路径

要求:

F:\study\javascript\regex\regular expression.pdf
F:\study\javascript\regex\
F:\study\javascript
F:\

分析:

  1. 整体的模式是: 盘符:\文件夹\文件夹\文件夹\
  2. 匹配 F:\ 使用 [a-zA-Z]:\\,其中盘符不区分大小写,注意 \ 需要使用转义
  3. 文件名和文件夹: [^\:*<>|"?\r\n/] 不能空名,至少一个字符,匹配文件夹: folder\. 可用 ([^\:*<>|"?\r\n/]+\\)+, 其中 括号提供子表达式
  4. 路径最后一部分可以是文件夹,没有 , 因此需要添加 ([^\:*<>|"?\r\n/]+)?
// 这正则很棒,最后一位的匹配直接使用 ? 来区分 folder\ 类型的

var regex = /^[a-zA-Z]:\\([^\\:*<|"?\r\n/]+\\)*([^\\:*<>|"?\r\n/]+)?$/;

regex.test('F:\\study\\javascript')
regex.test('F:\\study\\javascript\regex\\regular expression.pdf');
regex.test('F:\\study\\javascript\\regex\\');
// true
5.5 匹配 id

要求: 提取 id="container"

首先想到:

var regex = /id=".*"/;
var string = '<div id="container" class="main" style="color: red;"></div>';
console.log(string.match(regex)[0]);
// id="container" class="main" style="color: red;"

因为 . 是通配符, 本身就可以匹配双引号,而 量词 * 本身又是贪婪的, 当遇到 container 后面双引号时候,不会停下来,会继续匹配,直到遇到最后一个双引号位置

优化如下:

var regex = /id="[^"]*"/;
var string = '<div id="container" class="main" style="color: red;"></div>';
console.log(string.match(regex)[0]);
// id="container"