Open shaozj opened 5 years ago
在日常开发中,正则表达式常常能让人很困惑。实际上,掌握好正则表达式能让你的开发大大提效。废话不多说,直接开始学习吧。
正则表达式是用于匹配字符串中字符组合的模式。大部分语言都实现了正则表达式,而我们要研究的是 javascript 中的正则表达式。
有两种方式可以创建正则表达式:
语法:/pattern/[flags]
const re = /ab+c/; re.test('abbbc'); // true
flags 是可选的,将在下文中解释。
语法:new RegExp(pattern[, flags])
const re = new RegExp('ab+c'); re.test('abc'); //true
无论使用字面量的方式还是构造函数的方式,得到的都是正则表达式对象。
正则表达式主要有两个方法:
test 方法用于测试字符串是否匹配正则表达式,它返回 true 或者 false。例子:
const re = /hello/; re.test('hello world'); // true re.test('hell0 world'); // false
exec 方法用于在一个在字符串中执行查找匹配的RegExp,它返回一个数组(未匹配到则返回 null)。 exec 方法匹配成功后将返回一个数组并且更新正则表达式的属性。 先看一个例子:
const myRe = /d(b+)d/g; const myArray = myRe.exec("cdbbdbsbz"); // ["dbbd", "bb", index: 1, input: "cdbbdbsbz", groups: undefined]
// 命名捕获组的获取 let reg1 = /(?<first>\d)(?<second>\d)/; let str2 = '123'; console.log(reg2.exec(str2).groups); // => { first: 1, second: 2 } 而新的语法支持对这些捕获组进行命名, // 更方便地获取某个捕获组的数据
简单模式是由你想直接找到的字符构成。比如,/abc/ 这个模式就能且仅能匹配 "abc" 字符按照顺序同时出现的情况。
const regex = /hello/; console.log(regex.test('hello world')); // true
当你需要搜索一个比直接匹配需要更多条件的匹配时,比如寻找一个或多个 "b",或者寻找空格,这时可以在模式中使用特殊字符。比如,你可以使用 /ab*c/ 去匹配一个单独的 "a" 后面跟了零个或者多个 "b",同时后面跟着 "c" 的字符串:*的意思是前一项出现零次或者多次。在字符串 "cbbabbbbcdebc" 中,这个模式匹配了子字符串 "abbbbc"。
/ab*c/
下面列出了一个正则表达式中可以利用的特殊字符的完整列表和描述。
两个最重要的标识:
const re = /\w+\s/g; const str = "fee fi fo fum"; console.log(str.match(re)); // ["fee ", "fi ", "fo "] const re1 = /\w+\s/; console.log(str.match(re1)); // ["fee ", index: 0, input: "fee fi fo fum", groups: undefined]
注意上面例子中的 String.prototype.match 方法,当正则表达式带有 g 这个标识的时候,它返回的是所有匹配项组成的数组。如果正则表达式没有 g 这个标识,那么返回结果和 regexp 的 exec 方法一致。
const re = /abc/i; console.log(re.test('Abc')); // true
区分不同类型的字符,例如区分字母和数字。
. 小数点
.
/.n/
\d
/\d/
/[0-9]/
\D
/\D/
/[^0-9]/
\w
[A-Za-z0-9_]
/\w/
\W
[^A-Za-z0-9_]
/\W/
/[^A-Za-z0-9_]/
\s
\S
\t
\r
\n
\v
\f
[\b]
\0
\cX
\xhh
\uhhhh
\u{hhhh} or \u{hhhhh}
\
表示表达式字符的分组和范围。
x|y
/green|red/
[xyz] [a-c]
[xyz]
[a-c]
[abcd]
[a-d]
/[a-z.]+/
/[\w.]+/
[^xyz] [^a-c]
[^xyz]
[^a-c]
[^abc]
捕获括号 (x)
(x)
$1、$2、...、$n
'bar foo'.replace(/(...) (...)/, '$2 $1')
$&
\n 注意:n是一个具体数字
(?<Name>x)
// 命名捕获组的获取 let reg1 = /(?<first>\d)(?<second>\d)/ let str2 = '123' console.log(reg2.exec(str2).groups) // => { first: 1, second: 2 } 对这些捕获组进行命名,更方便地获取某个捕获组的数据
非捕获括号 (?:x)
(?:x)
/(?:foo){1,2}/.exec('foo'); // ["foo", index: 0, input: "foo", groups: undefined] /(foo){1,2}/.exec('foo'); // ["foo", "foo", index: 0, input: "foo", groups: undefined]
表示匹配的字符或表达式的数量。
*``**
{0,}
/bo*/
+
?
/e?le?/
/\d+/
/\d+?/
{n}
/a{2}/
{n,}
/a{2,}/
{n,m}
/a{1, 3}/
*`? +? ?? {n}? {n,}? {n,m}?`**
表示行和单词的开始和结尾。
匹配输入的开始 ^
^
/^A/
匹配输入的结束 $
$
/t$/
匹配一个词的边界 \b
\b
匹配一个非单词边界 \B
\B
/\B../
/y\B../
表示一个匹配在某些条件下发生。断言包含先行断言、后行断言和条件表达式。
先行断言 x(?=y)
x(?=y)
/Jack(?=Sprat)/
/Jack(?=Sprat|Frost)/
后行断言 (?<=y)x
(?<=y)x
/(?<=Jack)Sprat/
/(?<=Jack|Tom)Sprat/
正向否定查找 x(?!y)
x(?!y)
/\d+(?!\.)/
/\d+(?!\.)/.exec("3.141")
反向否定查找 (?<!y)x
(?<!y)x
/(?<!-)\d+/.exec('3')
/(?<!-)\d+/.exec('-3')
基于 unicode 字符属性区分字符。例如大写和小写字母、数学符号和标点。
'1234567'.replace(/\B(?=(\d{3})+(?!\d))/g, ','); // 1,234,567
用到了非单词边界、先行断言、正向否定查找、匹配数字、量词
/<span\b[^>]*>(.*?)<\/span>/ /<span\b[^>]*>(.*?)<\/span>/.exec('test') // null /<span\b[^>]*>(.*?)<\/span>/.exec('<span>test</span>') // ["<span>test</span>", "test"] /<span\b[^>]*>(.*?)<\/span>/.exec('<span class="x">test</span>') // ["<span class="x">test</span>", "test"]
javascript 正则表达式解析
在日常开发中,正则表达式常常能让人很困惑。实际上,掌握好正则表达式能让你的开发大大提效。废话不多说,直接开始学习吧。
什么是正则表达式
正则表达式是用于匹配字符串中字符组合的模式。大部分语言都实现了正则表达式,而我们要研究的是 javascript 中的正则表达式。
创建一个正则表达式
有两种方式可以创建正则表达式:
使用正则表达式字面量创建
flags 是可选的,将在下文中解释。
使用 RegExp 对象的构造函数创建
无论使用字面量的方式还是构造函数的方式,得到的都是正则表达式对象。
正则表达式的方法
正则表达式主要有两个方法:
RegExp.prototype.test()
test 方法用于测试字符串是否匹配正则表达式,它返回 true 或者 false。例子:
RegExp.prototype.exec()
exec 方法用于在一个在字符串中执行查找匹配的RegExp,它返回一个数组(未匹配到则返回 null)。 exec 方法匹配成功后将返回一个数组并且更新正则表达式的属性。
先看一个例子:
返回的数组
更新正则表达式属性
正则表达式的模式
简单模式
简单模式是由你想直接找到的字符构成。比如,/abc/ 这个模式就能且仅能匹配 "abc" 字符按照顺序同时出现的情况。
使用特殊字符
当你需要搜索一个比直接匹配需要更多条件的匹配时,比如寻找一个或多个 "b",或者寻找空格,这时可以在模式中使用特殊字符。比如,你可以使用
/ab*c/
去匹配一个单独的 "a" 后面跟了零个或者多个 "b",同时后面跟着 "c" 的字符串:*的意思是前一项出现零次或者多次。在字符串 "cbbabbbbcdebc" 中,这个模式匹配了子字符串 "abbbbc"。下面列出了一个正则表达式中可以利用的特殊字符的完整列表和描述。
标识 flags
两个最重要的标识:
注意上面例子中的 String.prototype.match 方法,当正则表达式带有 g 这个标识的时候,它返回的是所有匹配项组成的数组。如果正则表达式没有 g 这个标识,那么返回结果和 regexp 的 exec 方法一致。
字符类别(Character Classes)
区分不同类型的字符,例如区分字母和数字。
.
小数点/.n/
将会匹配 "nay, an apple is on the tree" 中的 'an' 和 'on',但是不会匹配 'nay'。\d
/\d/
或者/[0-9]/
匹配"B2 is the suite number."中的'2'。\D
/\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
\S
\t
\r
\n
\v
\f
[\b]
\0
\cX
\xhh
\uhhhh
\u{hhhh} or \u{hhhhh}
\
组和范围(Groups and Ranges)
表示表达式字符的分组和范围。
x|y
/green|red/
匹配“green apple”中的‘green’和“red apple”中的‘red’[xyz]
[a-c]
[abcd]
和[a-d]
是一样的。他们都匹配"brisket"中的‘b’,也都匹配“city”中的‘c’。/[a-z.]+/
和/[\w.]+/
与字符串“test.i.ng”匹配。[^xyz]
[^a-c]
[^abc]
和[^a-c]
是一样的。他们匹配"brisket"中的‘r’,也匹配“chop”中的‘h’。捕获括号
(x)
$1、$2、...、$n
这样的语法,例如,'bar foo'.replace(/(...) (...)/, '$2 $1')
。$&
表示整个用于匹配的原字符串。\n
注意:n是一个具体数字(?<Name>x)
非捕获括号
(?:x)
量词(Quantifiers)
表示匹配的字符或表达式的数量。
*``**
{0,}
。/bo*/
会匹配 "A ghost boooooed" 中的 'booooo' 和 "A bird warbled" 中的 'b',但是在 "A goat grunted" 中不会匹配任何内容。+
?
/e?le?/
匹配 "angel" 中的 'el'、"angle" 中的 'le' 以及 "oslo' 中的 'l'。/\d+/
将会匹配 "123",而使用/\d+?/
则只会匹配到 "1"。{n}
/a{2}/
不会匹配“candy”中的'a',但是会匹配“caandy”中所有的 a,以及“caaandy”中的前两个'a'。{n,}
/a{2,}/
匹配 "aa", "aaaa" 和 "aaaaa" 但是不匹配 "a"。{n,m}
/a{1, 3}/
并不匹配“cndy”中的任意字符,匹配“candy”中的a,匹配“caandy”中的前两个a,也匹配“caaaaaaandy”中的前三个a。注意,当匹配”caaaaaaandy“时,匹配的值是“aaa”,即使原始的字符串中有更多的a。*`? +? ?? {n}? {n,}? {n,m}?`**
?
如果紧跟在任何量词 *、 +、? 或 {} 的后面,将会使量词变为非贪婪(匹配尽量少的字符),和缺省使用的贪婪模式(匹配尽可能多的字符)正好相反。例如,对 "123abc" 使用 /\d+/ 将会匹配 "123",而使用 /\d+?/ 则只会匹配到 "1"。边界(Boundaries)
表示行和单词的开始和结尾。
匹配输入的开始
^
/^A/
并不会匹配 "an A" 中的 'A',但是会匹配 "An E" 中的 'A'。[^xyz]
反向字符集,匹配任何没有包含在方括号中的字符。匹配输入的结束
$
/t$/
并不会匹配 "eater" 中的 't',但是会匹配 "eat" 中的 't'。匹配一个词的边界
\b
匹配一个非单词边界
\B
/\B../
匹配"noonday"中的'oo', 而/y\B../
匹配"possibly yesterday"中的’yes‘断言(Assertions)
表示一个匹配在某些条件下发生。断言包含先行断言、后行断言和条件表达式。
先行断言
x(?=y)
/Jack(?=Sprat)/
会匹配到'Jack'仅仅当它后面跟着'Sprat'。/Jack(?=Sprat|Frost)/
匹配‘Jack’仅仅当它后面跟着'Sprat'或者是‘Frost’。但是‘Sprat’和‘Frost’都不是匹配结果的一部分。'后行断言
(?<=y)x
/(?<=Jack)Sprat/
会匹配到'Sprat'仅仅当它前面是'Jack'。/(?<=Jack|Tom)Sprat/
匹配'Sprat'仅仅当它前面是'Jack'或者是'Tom'。但是'Jack'和'Tom'都不是匹配结果的一部分。正向否定查找
x(?!y)
/\d+(?!\.)/
匹配一个数字。正则表达式/\d+(?!\.)/.exec("3.141")
匹配'141'而不是'3.141'反向否定查找
(?<!y)x
/(?<!-)\d+/.exec('3')
匹配到 "3"./(?<!-)\d+/.exec('-3')
因为这个数字前有负号,所以没有匹配到。Unicode 属性转义(Unicode Property Escapes)
基于 unicode 字符属性区分字符。例如大写和小写字母、数学符号和标点。
例子
用到了非单词边界、先行断言、正向否定查找、匹配数字、量词
参考文献: