之前是写在另一个平台,但更新的断断续续,这次打算回炉重造,整理一波。

1.创建正则表达式

两种方式:

  • 字面量:由斜杠包围而不是引号包围
  • 构造函数的字符串参数:由引号而不是斜杠包围
// 使用正则表达字面量
var regex = /ab+c/;
// 使用RegExp对象的构造函数
var regex = new RegExp("ab+c");

2.正则匹配

正则表达式的精髓,主要在与它的模糊。先来看一个基础小例子:

var regex = /hello/;
console.log(regex.test("hello"));  
// => true
var string = 'Hi, hello world';
console.log(string.match(regex));
// => ["hello", index: 4, input: "Hi, hello world", groups: undefined]

2.1 量词匹配

{m,n},表示连续出现最少m次,最多n次。

例如:/ab{2,5}c/表示:第一个字符是“a”,第2到5个字符“b”,最后是字符“c”。

这里可以看出{m,n}作用的是前面的那个字符,即b。

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表示全局匹配,即在目标字符串中按顺序找到满足匹配模式的所有子串,强调的是“所有”,而不只是“第一个”。

量词表示匹配的字符或表达式的数量,下表中是一些简单常用的形式:

字符

含义

{m,n}

n 和 m 都是整数。匹配前面的字符至少 m 次,最多 n 次。如果 n 或者 m 的值是0, 这个值被忽略。

例如,/a{1, 3}/ 并不匹配“cndy”中的任意字符,匹配“candy”中的a,匹配“caandy”中的前两个a,也匹配“caaaaaaandy”中的前三个a。注意,当匹配”caaaaaaandy“时,匹配的值是“aaa”,即使原始的字符串中有更多的a。

{m,}

m是一个正整数,匹配前一个字符至少出现了m次。

例如, /a{2,}/ 匹配 "aa", "aaaa" 和 "aaaaa" 但是不匹配 "a"。

{m}

m 是一个正整数,匹配了前面一个字符刚好出现了 m 次。等价于{m,m}

比如, /a{2}/ 不会匹配“candy”中的'a',但是会匹配“caandy”中所有的 a,以及“caaandy”中的前两个'a'。

?

匹配前面一个表达式 0 次或者 1 次。等价于 {0,1}

例如,/e?le?/ 匹配 "angel" 中的 'el'、"angle" 中的 'le' 以及 "oslo' 中的 'l'。

如果紧跟在任何量词 *、 +、? 或 {} 的后面,将会使量词变为非贪婪(匹配尽量少的字符),和缺省使用的贪婪模式(匹配尽可能多的字符)正好相反。例如,对 "123abc" 使用 /\d+/ 将会匹配 "123",而使用 /\d+?/ 则只会匹配到 "1"。

+

匹配前面一个表达式 1 次或者多次。等价于 {1,}

例如,/a+/ 会匹配 "candy" 中的 'a' 和 "caaaaaaandy" 中所有的 'a',但是在 "cndy" 中不会匹配任何内容。

*

匹配前一个表达式 0 次或多次。等价于 {0,}

例如,/bo*/ 会匹配 "A ghost boooooed" 中的 'booooo' 和 "A bird warbled" 中的 'b',但是在 "A goat grunted" 中不会匹配任何内容。

2.2 字符组匹配

[abc],表示表示匹配一个字符,可以是字符‘a’、‘b’、‘c’中的任何一个。

比如/a[123]b/可以匹配如下三种字符串:"a1b"、"a2b"、"a3b"。

var regex = /a[123]b/g;
var string = "a0b a1b a2b a3b a4b";
console.log(string.match(regex)); 
// => ["a1b", "a2b", "a3b"]

问:如果字符组里的字符特别多,怎么办?
答:可以使用范围表示法。比如[123456abcdefGHIJKLM],可以写成[1-6a-fG-M]。用连字符-来省略和简写。

问:因为连字符有特殊用途,如果要匹配“a”、“-”、“z”这三者中任意一个字符,怎么办?
答:不能写成[a-z],因为其表示小写字符中的任何一个字符。可以写成如下的方式:[-az][az-][a\-z]。三条路:开头、结尾、转义。

问: 如果我想要的匹配的字符是除了a/b/c三个字符之外的,怎么办?
答:使用反向字符集。例如[^abc],表示除"a"、"b"、"c"之外的任意一个字符。字符组的第一位放^,表示取反。当然,也可以使用范围表示。

关于字符组常见的简写形式,引用MDN上的常用的字符符号:

字符

含义

\d

等价于[0-9] ,匹配的是一个数字。

例如, /\d/ 或者 /[0-9]/ 匹配"B2 is the suite number."中的'2'。

\D

等价于 [^0-9] ,匹配的是一个非数字字符。

例如, /\D/ 或者 /[^0-9]/ 匹配"B2 is the suite number."中的'B' 。

\w

等价于 [A-Za-z0-9_],匹配的是字母、数字或者下划线。

例如, /\w/ 匹配 "apple," 中的 'a',"$5.28,"中的 '5' 和 "3D." 中的 '3'。

\W

等价于 [^A-Za-z0-9_],匹配一个非单字字符。

例如, /\W/ 或者 /[^A-Za-z0-9_]/ 匹配 "50%." 中的 '%'。

\s

匹配一个空白字符,包括空格、制表符、换页符和换行符。
等价于[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。

例如, /\s\w*/ 匹配"foo bar."中的' bar'。

经测试,\s不匹配"\u180e",在当前版本Chrome(v80.0.3987.122)和Firefox(76.0.1)控制台输入/\s/.test("\u180e")均返回false。

\S

匹配一个非空白字符。
等价于 [^\f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]。

例如,/\S\w*/ 匹配"foo bar."中的'foo'。

.

(小数点)默认匹配除换行符之外的任何单个字符。

例如,/.n/ 将会匹配 "nay, an apple is on the tree" 中的 'an' 和 'on',但是不会匹配 'nay'。

如果 s ("dotAll") 标志位被设为 true,它也会匹配换行符。

问:如果要匹配任意字符怎么办?
答:可以使用[\d\D][\w\W][\s\S][^]中任何的一个。

再来看两个小例子吧:

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位、5位连续数字。这个例子的特点就是:尽可能的多匹配。换句话说,就是贪婪。

而加上问号的它,就会尽可能少的匹配:

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次都行,当两个可以的时候,就不往下面匹配了。

因此,量词后面加问号,也可以理解为有人问你:够了不?够了就行了。适可而止。

3. 多选分支

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

例如要匹配"happy"和"hobby"可以使用/happy|hobby/。测试如下:

var regex = /happy|hobby/g;
var string = "happy time, hobby";
console.log( string.match(regex) ); 
// => ["happy", "hobby"]

但是,比如我用/hello|helloWorld/,去匹配"helloWorld"字符串时,结果是"hello":

var regex = /hello|helloWorld/g;
var string = "helloWorld";
console.log( string.match(regex) ); 
// => ["hello"]

而把正则改成/helloWorld|hello/,结果是:

var regex = /helloWorld|hello/g;
var string = "helloWorld";
console.log( string.match(regex) ); 
// => ["helloWorld"]

同上面的?效果一样,即当前面的匹配上了,后面的就不再尝试了。

4. 案例分析

注意,每个正则并不是只有唯一写法:

4.1 匹配16进制颜色值

要求匹配:

#ff8040

#d1d3D8

#F4F6F9

#fffEEF

#FFF

分析:

表示一个16进制字符,可以用字符组[0-9a-fA-F]

其中字符可以出现3或6次,需要是用量词和分支结构。

使用分支结构时,需要注意顺序。

正则如下:

var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
var string = "#ff8040 #d1d3D8 #F4F6F9 #fffEEF #FFF";
console.log(string.match(regex)); 
// => ["#ff8040", "#d1d3D8", "#F4F6F9", "#fffEEF", "#FFF"]

4.2 匹配日期

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

要求匹配:

2020-11-19

分析:

年,四位数字即可,可用[0-9]{4}

月,共12个月,分两种情况01、02、……、09和10、11、12,可用(0[1-9]|1[0-2])

日,最大31天,可用(0[1-9]|[12][0-9]|3[01])

正则如下:

// 注意:此正则没有对2月和30、31号做单独的校验处理
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-10")); 
// => true
// 	关于$: 匹配输入的结束。如果多行标志被设置为 true,那么也匹配换行符前的位置。
//	例如,/t$/ 并不会匹配 "eater" 中的 't',但是会匹配 "eat" 中的 't'。
//  关于^: 匹配输入的开始。如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置。
//	例如,/^A/ 并不会匹配 "an A" 中的 'A',但是会匹配 "An E" 中的 'A'。

参考:正则表达式系列总结 - 知乎

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐