前言:每次用到正则表达式都是百度,很不方便,并且测试好多次才能达到想要的效果。正则表达式的规则不难,但是要想用好却不容易,主要还是一个“熟能生巧”。所以专门花一些时间自己动手实现一下常用的正则表达式。主要参考菜鸟教程
正则表达式
- 正则表达式是什么?
1、正则表达式(Regular Expression)是一种文本模式,提供字符串匹配规则。正则表达式包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。
2、特殊字符按用途可以分为两类,一种是用来修饰普通字符的,比如*
,必须和普通字符连用才能发挥它的价值;一种是使用\
转义为特殊字符的,这种用起来和普通字符是一样的,只不过能匹配上的子串比较多。 - 正则表达式能做什么?
1、查找
2、替换
3、提取 - 碎碎念
正则表达式的难点在于特殊字符的分类使用方面。虽然都是特殊字符,但是通过用法可以分为好几类。在使用的时候需要明确所使用的特殊字符的作用。所以这里将特殊字符根据其用途分为了几类,但其中可能有交集。这里我只研究了本人在前端开发中能用得到的。
一、特殊字符
(一)非打印字符
字符 | 描述 |
---|---|
\n | 匹配换行符。 |
\r | 匹配回车符。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。 |
\S | 匹配任何非空白字符。 |
\t | 匹配制表符。就是tab |
(二)限定符
限定符需要跟在一个表达式的后面,用于规定匹配前面的表达式的次数
字符 | 描述 |
---|---|
* | 子表达式出现0次或多次 |
+ | 子表达式出现一次或多次 |
? | 子表达式出现零次或一次 |
{n} | n 是一个非负整数,出现n次 |
{n,} | n 是一个非负整数,至少出现n次 |
{n,m} | n 、m 都是非负整数,并且n<m,出现[n,m]次 |
重点解析?
?
有两种用法
- 一种是放在子表达式的后面,表示该子表达式出现零次或一次
- 第二种是放在其他的限定符后面,此时匹配规则会变成非贪婪模式。即尽量较少的匹配字符。正则匹配默认是贪婪模式,举个例子看两者的区别:
'apppp'.match(/a[p]+/) // 贪婪模式,尽可能多的匹配字符
// 输出
['apppp', index: 0, input: 'apppp', groups: undefined]
// --------------------------------------------------------------------------------------------------
'apppp'.match(/a[p]+?/) // 非贪婪模式,尽可能少的匹配字符
// 输出
['ap', index: 0, input: 'apppp', groups: undefined]
(三)定位符
定位符用来规定特定位置的匹配规则
字符 | 描述 |
---|---|
^ | 匹配字符串开始的位置,后面跟匹配规则,如/^[0-9]/ 说明以数字开头 |
$ | 匹配字符串结尾的位置,前面跟匹配规则,如hhh$ 表示以“hhh”这个字符串结尾 |
\b | 匹配字符串的边界,即字符前面或后面有空格,如果是/\bcha/ 会从字符串开头找;如果是ter\b 会从字符串结尾找 |
\B | 匹配字符串的非边界,即在字符串中间进行匹配,不可匹配单词的开头和结尾,\B 写匹配规则的前面后面都一样 |
例子: |
'chapterdfs'.match(/\bcha/) => ["cha"] (1) //在开头找cha子串
// --------------------------------------------------------------------------------------------------
'chapterdfs'.match(/dfs\b/) => ["dfs"] (1) //在结尾找dfs子串
// --------------------------------------------------------------------------------------------------
'chapterdfs'.match(/\Bcha/) => null //cha子串在边界,所以使用\B找不到
// --------------------------------------------------------------------------------------------------
'chapterdfs'.match(/\Bha/) => ["ha"] (1) //在字符串的中间找"ha"
(四)通过 \
+英文字母 转换的特殊字符
已经讲过的字符就不再赘述(\n
、\s
、\r
、\s
、\S
、\t
、\b
、\B
)
字符 | 描述 |
---|---|
\d | 匹配数字,等价于[0,9] |
\D | 匹配非数字 |
\w | 匹配字母、数字、下划线 |
\W | 匹配非字母、数字、下划线 |
(五)特殊字符的组合技
已经讲过的字符就不再赘述({n}
、{n,}
、{n,m}
)
字符 | 描述 |
---|---|
[abc] | 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 “plain” 中的 ‘a’。 |
[^abc] | 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 “plain” 中的’p’、‘l’、‘i’、‘n’。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如, '[a-z]' 可以匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符。 |
[^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,‘[^a-z]’ 可以匹配任何不在 ‘a’ 到 ‘z’ 范围内的任意字符。 |
x竖线y | 匹配x或y |
() | 表示里面的内容是一个表达式,类似于数学中的()的作用。 |
重点解析()
在不使用修饰符的情况下使用match方法,返回的是一个类数组对象
- (pattern) 方法
'oko4gyhjgjghj5gh'.match(/([k-o]|jhljil)[1-9]/)
// 输出
(2) ['o4', 'o', index: 2, input: 'oko4gyhjgjghj5gh', groups: undefined]
第一个元素是通过匹配规则的子串,第二个元素是真正起作用的匹配规则;这里[k-o]
是匹配从k到o的字母,但真正匹配上的就是o
这个字符
- (?:pattern)
'oko4gyhjgjghj5gh'.match(/(?:[k-o]|jhljil)[1-9]/)
// 输出
['o4', index: 2, input: 'oko4gyhjgjghj5gh', groups: undefined]
只返回通过匹配规则的子串信息,不返回真正起作用的匹配规则
- (?=pattern) 正向肯定预查
匹配顺序从左向右,用于匹配()前面的子串,即一个子串后面跟的内容符合这个匹配规则,那么就可以找到这个子串
'window7777'.match(/[a-z](?=[0-9])/) //匹配后面跟数字的字母
// 输出
['w', index: 5, input: 'window7777', groups: undefined]
'windowhhh'.match(/[a-z](?=[0-9])/)
// 输出
null
- (?!pattern) 正向否定预查
匹配规则从左向右,用于匹配一个后面不跟特定内容的子串
'windowhhh'.match(/[a-z](?![0-9])/) // 匹配后面跟的不是数字的字母
// 输出
['w', index: 0, input: 'windowhhh', groups: undefined]
// --------------------------------------------------------------------------------------------------
'window999'.match(/[a-z](?![a-z])/) // 匹配后面跟的不是字母的字母
// 输出
['w', index: 5, input: 'window999', groups: undefined]
- (?<=pattern) 反向肯定预查
与正向肯定预查类似,但是方向相反,放在要找的子串的前面,如果一个子串前面的内容可以匹配上规则,那么就可以找到这个子串
'This is an apple.'.match(/(?<=\s)[a-z]+/) // 匹配前面是空格的字母串
// 输出
['is', index: 5, input: 'This is an apple.', groups: undefined]
- (?<!pattern) 反向否定预查
子串前面的内容不符合规则时可以找到这个子串
'This is an apple.'.match(/(?<!\s)[a-z]+/) // 匹配前面不是空格的字母串
// 输出
['his', index: 1, input: 'This is an apple.', groups: undefined]
(六)通配符
还有一个漏网之大鱼
字符 | 描述 |
---|---|
. | 匹配除换行符(\n、\r)之外的任何单个字符 |
二、修饰符
修饰符用于指定额外的匹配策略,对于正则表达式的整体起作用
修饰符 | 描述 |
---|---|
i | ignore - 不区分大小写 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。 |
g | global - 全局匹配 查找所有的匹配项。 |
m | multi line - 多行匹配 |
s | 特殊字符圆点 . 中包含换行符 \n 默认情况下的圆点 . 是匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。 |
重点解析
I和g比较简单就不举例子了,重点看m
和s
m
需要在node环境中测试,浏览器中\n会被识别成普通字符串
'amp\napp'.match(/pp/m) // 多行匹配pp字符串
//输出 \n也会被计数,所以index是5
(1) ['pp', index: 5, input: 'amp
app', groups: undefined]
s
默认.
是匹配除了\n、\r之外的任意字符,使用s
修饰符后,.
会匹配包括\n、\r的所有字符
'\nappmbapp'.match(/.(?=app)/) // 匹配后面跟app的一个字符
// 输出
(1) ['b', index: 5, input: '
appmbapp', groups: undefined]
// --------------------------------------------------------------------------------------------------
'\nappmbapp'.match(/.(?=app)/s) // 使`.`能匹配上换行
// 输出
(1) ['
', index: 0, input: '
appmbapp', groups: undefined]
三、运算符优先级
正则表达式从左到右进行计算,并遵循优先级顺序,这与算术表达式非常类似。
相同优先级的从左到右进行运算,不同优先级的运算先高后低。下表从最高到最低说明了各种正则表达式运算符的优先级顺序:
运算符 | 描述 |
---|---|
\反斜杠 | 转义符 |
(), ( ?: ), (?=), [] | 圆括号和方括号 |
*, +, ?, {n}, {n,}, {n,m} | 限定符 |
^, $, \元字符(匹配作为作为普通字符的特殊字符,比如\* 匹配*这个字符),普通字符 | 表示位置和顺序 |
竖线 | 替换运算符,表示"或"操作 |