JavaScript正则表达式进阶学习

写在前面

作为一个有理想有追求的码农,能够风骚地使用正则表达式着实是一大快事。在别人还用着循规蹈矩的方式去写业务代码时,高级的正则却可以丢上一段简短的乱码优雅地解决问题,确实很风骚,不过也不能乱用,用前权衡一下可读性和可维护性吧。
众所周知,正则表达式主要用于匹配字符串中字符组合。在 JavaScript中,正则表达式也是对象。这些模式被用于 RegExpexectest 方法, 以及 Stringmatchreplacesearchsplit 方法。
本文主要讨论正则的用法,包括常见和进阶用法,侧重高级用法,以及尽量通俗化地解释正则表达式中一些高深概念。

常见用法

一图胜过万千言语,思路非常清晰的思维导图整理,熟悉的可以跳过这部分
https://segmentfault.com/u/sushi

以上为一个系统的概论,对于本人,平时用到的主要有:

. 匹配除换行符之外的任何单个字符

示例:表达式 a.\d,在匹配 “aaa100” 时,匹配的结果是:成功;匹配到的内容是:”aa1”;匹配到的位置是:开始于1,结束于4。

用法:/<div>(.*?)<\/div>/可以匹配div标签内有换行的数据。另外如果想匹配包含换行的全部字符,可以使用[\s\S]代替.

\s 匹配一个空白字符

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

示例: /\S+/ 匹配”abc 123 @#$%”

用法:[\s\S]表示匹配任意字符,\S是\s的反义。注意区分[\s\S]与.的区别。

\ 转义

在表达式中有特殊意义,需要添加 “\” 才能匹配该字符本身的字符汇总表

字符 说明
^ 匹配输入字符串的开始位置。要匹配 “^” 字符本身,请使用 “\^”
$ 匹配输入字符串的结尾位置。要匹配 “$” 字符本身,请使用 “\$”
( ) 标记一个子表达式的开始和结束位置。要匹配小括号,请使用 “(“ 和 “)“
[ ] 用来自定义能够匹配 ‘多种字符’ 的表达式。要匹配中括号,请使用 “[“ 和 “]“
{ } 修饰匹配次数的符号。要匹配大括号,请使用 “{“ 和 “}“
. 匹配除了换行符(\n)以外的任意一个字符。要匹配小数点本身,请使用 “.“
? 修饰匹配次数为 0 次或 1 次。要匹配 “?” 字符本身,请使用 “\?”
+ 修饰匹配次数为至少 1 次。要匹配 “+” 字符本身,请使用 “+“
* 修饰匹配次数为 0 次或任意次。要匹配 “*“ 字符本身,请使用 “\*“
| 左右两边表达式之间 “或” 关系。匹配 “|” 本身,请使用 “\|”

常用:

* 匹配零个或更多个,即0~n
+ 匹配一个或更多个,即至少一个,1~n
^ 字符串开始
$ 字符串结束

[]用法

使用方括号[ ]包含一系列字符,能够匹配其中任意一个字符。

表达式 可匹配
[ab5@] 匹配 “a” 或 “b” 或 “5” 或 “@”
[^abc] 匹配 “a”,”b”,”c” 之外的任意一个字符
[f-k] 匹配 “f”~”k” 之间的任意一个字母,如果要包含-字符,可以给它加上转义[\-]
[^A-F0-3] 匹配 “A”~“F”,”0”~“3” 之外的任意一个字符

示例:表达式 “[bcd][bcd]“ 匹配 “abc123” 时,匹配的结果是:成功;匹配到的内容是:”bc”;匹配到的位置是:开始于1,结束于3。

关于[]常见的错误用法是:[ab|bc]用来表示abbc,实际上,它得到的结果是[abc|],即a或b或c或|这4个字符(单字符)的任意一个。这里可以改成(ab|bc)
总结:[]里面的特殊符有五个:[]-\^,其他字符都是普通字符,包括*.?等。

次数修饰{}

使用方法是:”次数修饰”放在”被修饰的表达式”后边。比如:”[bcd][bcd]” 可以写成 “[bcd]{2}”。

表达式 作用
{n} 表达式重复n次。 “\w{2}” 相当于 “\w\w”; “a{5}” 相当于 “aaaaa”
{m,n} 表达式至少重复m次,最多重复n次。 “ba{1,3}”可以匹配 “ba”或”baa”或”baaa”
{m,} 表达式至少重复m次。 “\w\d{2,}”可以匹配 “a12”,”_456”,”M12344”
? 匹配表达式0次或者1次,相当于 {0,1}
+ 表达式至少出现1次,相当于 {1,}
* 表达式不出现或出现任意次,相当于 {0,}

如果刚好需要匹配字符{1},则正则需要给{进行转义,得到\{1}的正则。
如果{}中间不是数字,则{}本身不需要转义。

PS:这里?也可以用于非贪婪模式。

非贪婪模式
在修饰匹配次数的特殊符号后再加上一个 “?” 号,则可以使匹配次数不定的表达式尽可能少的匹配,使可匹配可不匹配的表达式,尽可能的 “不匹配”。这种匹配原则叫作 “非贪婪” 模式,也叫作 “勉强” 模式。如果少匹配就会导致整个表达式匹配失败的时候,与贪婪模式类似,非贪婪模式会最小限度的再匹配一些,以使整个表达式匹配成功。

| 多个数据选一(常用于多字符)

对比[]里面的只能选一个字符功能,|常用于选取多个字符。
比如:http|ftp|svn 就需要用|分开,|的作用域是一直往后直到遇到括号,比如,对于源字符串http|ftp|svn abc匹配的结果是:’http’,’ftp’,’svn abc’。
想要匹配 http abcftp abcsvn abc就要使用括号把前边的协议括起来,如(http|ftp|svn) abc 可以得到预期的结果。

()用法

字符 含义
(x) 匹配 ‘x’ 并且记住匹配项,就像下面的例子展示的那样。括号被称为 捕获括号 。模式/(foo) (bar) \1 \2/中的 ‘(foo)’ 和 ‘(bar)’ 匹配并记住字符串 “foo bar foo bar” 中前两个单词。模式中的 \1 和 \2 匹配字符串的后两个单词。注意 \1、\2、\n 是用在正则表达式的匹配环节。在正则表达式的替换环节,则要使用像 $1、$2、$n 这样的语法,例如,’bar foo’.replace( /(…) (…)/, ‘$2 $1’ )。
(?:x) 匹配 ‘x’ 但是不记住匹配项。这种叫作非捕获括号,使得你能够定义为与正则表达式运算符一起使用的子表达式。来看示例表达式 /(?:foo){1,2}/。如果表达式是 /foo{1,2}/,{1,2}将只对 ‘foo’ 的最后一个字符 ’o‘ 生效。如果使用非捕获括号,则{1,2}会匹配整个 ‘foo’ 单词。
x(?=y) 匹配’x’仅仅当’x’后面跟着’y’.这种叫做正向肯定查找。例如,/Jack(?=Sprat)/会匹配到’Jack’仅仅当它后面跟着’Sprat’。/Jack(?=Sprat|Frost)/匹配‘Jack’仅仅当它后面跟着’Sprat’或者是‘Frost’。但是‘Sprat’和‘Frost’都不是匹配结果的一部分。
x(?!y) 匹配’x’仅仅当’x’后面不跟着’y’,这个叫做正向否定查找。例如,/\d+(?!.)/匹配一个数字仅仅当这个数字后面没有跟小数点的时候。正则表达式/\d+(?!.)/.exec(“3.141”)匹配‘141’但是不是‘3.141’

模式/(foo) (bar) \1 \2/中的 ‘(foo)’ 和 ‘(bar)’都分别为子模式,匹配结果会得到一个[1][2]的子集数据(数组下标分别是1、2)。子模式也叫分组,利用子模式,可以得到想要取出来的数据。子模式1、2、3的计算方法为左括号的计数,从左到右,从1开始,比如上面的分组1得到的是(foo)里面的数据,分组2得到(bar)里面的数据。注意这里的非捕获组和断言的左括号都是不需要数的。

常用正则匹配汇总

常用的一下正则表达式的汇总——传送门,欢迎修正补充。

进阶学习

常用对象方法

用法 说明 返回值
pattern.test(str) 判断str是否包含匹配结果 包含返回true,不包含返回false
pattern.exec(str) 根据patternstr进行正则匹配 返回匹配结果数组,如匹配不到返回null
str.match(pattern) 根据patternstr进行正则匹配 返回匹配结果数组,如匹配不到返回null
str.replace(pattern, replacement) 根据pattern进行正则匹配,把匹配结果替换为replacement 一个新的字符串

exec()

例子:

1
2
3
4
5
// Match "quick brown" followed by "jumps", ignoring characters in between
// Remember "brown" and "jumps"
// Ignore case
var re = /quick\s(brown).+?(jumps)/ig;
var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
对象 属性/索引 描述 例子
result [0] 匹配的全部字符串 Quick Brown Fox Jumps
[1], ...[_n_] 括号中的分组捕获 [1] = Brown [2] = Jumps
index 匹配到的字符位于原始字符串的基于0的索引值 4
input 原始字符串 The Quick Brown Fox Jumps Over The Lazy Dog
re lastIndex 下一次匹配开始的位置 25
ignoreCase 是否使用了 “i“ 标记使正则匹配忽略大小写 true
global 是否使用了 “g“ 标记来进行全局的匹配. true
multiline 是否使用了 “m“ 标记使正则工作在多行模式(也就是,^ 和 $ 可以匹配字符串中每一行的开始和结束(行是由 \n 或 \r 分割的),而不只是整个输入字符串的最开始和最末尾处。) false
source 正则匹配的字符串 quick\s(brown).+?(jumps)

其中的lastIndex属性有两种情况需要考虑:

  1. 非全局匹配
1
2
3
4
5
6
7
8
9
10
var reg = /\d/;
// 第一次匹配
console.log(reg.exec('a123')); // ["1"]
console.log(reg.lastIndex); // 0

// 第二次匹配
console.log(reg.exec('a123')); // ["1"]
console.log(reg.lastIndex); // 0

// 同一正则表达式,在非全局匹配模式下,每次实例的lastIndex属性的值总是不变的(为第一次找到匹配文本所在的位置,上面为0 );
  1. 全局匹配
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var reg = /\d/g;
// 第一次匹配
console.log(reg.exec('a123')); // ["1"]
console.log(reg.lastIndex); // 2

// 第二次匹配
console.log(reg.exec('a123')); // ["2"]
console.log(reg.lastIndex); // 3

// 第三次匹配
console.log(reg.exec('a123')); // ["3"]
console.log(reg.lastIndex); // 4

// 第四次匹配
console.log(reg.exec('a123')); // null
console.log(reg.lastIndex); // 0

// 当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。

match()

  1. 非全局匹配
1
2
var a = 'aaaa'.match(/\w/);
console.log(a); // ["a", index: 0, input: "aaaa"]

可以看到,和exec()一样,在数组中返回了index 和 input属性。

  1. 全局匹配
1
2
var a = 'aaaa'.match(/\w/g);
console.log(a); // ["a", "a", "a", "a"]

全局匹配就和exec方法有很大的不同了,他直接返回了所有符合匹配的子字符串的数组,另外,index和input属性也不在其中了,所以这个方法效率可能会高一些,但是如果你需要更多的信息,则用exec()

replace()
replacement既可以是字符串也可以是函数,作为函数时可以传入两个参数(value,index)。

replacement 中的 $ 字符具有特定的含义:
| 字符 | 替换文本 |
| — | — |
| 1、2、…、$99 | 与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。 |
| $& | 与 regexp 相匹配的子串 |
| $` | 位于匹配子串左侧的文本 |
| $’ | 位于匹配子串右侧的文本 |
| $$ | 直接量符号 |

结语

未完待续…

参考

  1. http://www.regexlab.com/zh/regref.htm
  2. http://www.zjmainstay.cn/my-regexp
  3. https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
  4. https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F
-------------The End-------------

本文标题:JavaScript正则表达式进阶学习

文章作者:Alvabill

发布时间:2018年05月02日 - 00:05

最后更新:2018年09月09日 - 15:09

原始链接:http://alvabill.ml/JavaScript正则表达式进阶学习/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。