为了账号安全,请及时绑定邮箱和手机立即绑定

吃透js中正则表达式

我们在开发的过程中总会碰到一些需求需要做字符串匹配,当遇到一些稍微复杂一点的匹配规则时,如果我们对正则还不那么清晰,我们总是会去网上搜索一些现成的正则匹配,ctrl+c,ctrl+v。时间长了我们对这种修修补补的方式不厌其烦。那么今天就对正则表达式做足功夫,磨刀不误砍柴工,首先推荐一个学习正则的一个可视化的工具网址: https://regexper.com,输入正则规则即可生成匹配规则的流程图。了解并吃透正则会为我们以后开发节省很多时间和精力,一劳永逸。先从最基本的概念开始吧,let go!

元字符

概念:有特殊含义的特殊字符,eg: * + ? $ ^ . | \ ( ) { } [ ]等

通用含义元字符

在大部分语言语境中代表同一种含义,如下表:

字符 含义
\t 水平制表符
\v 垂直制表符
\n 换行符
\r 回车符
\0 空字符
\f 换页符
\cX Ctrl+X

其他含义元字符

1. 字符类
  • 我们可以使用元字符[]来构建一个简单的字符类
  • 所谓的类是指符合某些特性的对象,一个泛指,而不是特指某个字符
  • 表达式[abc]归为一类,表达式可以匹配这些类的字符
2. 字符类取反
  • 我们可以使用元字符^来创建反向类/负向类
  • 反向类的意思是不属于某个类的内容
  • 表达式[^abc]表示不是字符a或b或c的内容
3. 范围类
  • 正则表达式还提供了范围类
  • 我们使用[a-z]来连接两个字符表示从a到z的任意字符
  • 这是个闭区间也就包含a和z本身
4. 预定义类
  • 正则表达式提供了一些特殊字符表示自一组比较复杂的匹配规则,如下表:
字符 等价 含义
. [^\r\n] 除了回车符和换行符之外的所有字符
\d [0-9] 数字字符
\D [^0-9] 非数字字符
\s [\t\n\x0B\f\r] 空白符
\0 [^\t\n\x0B\f\r] 非空白符
\w [a-zA-Z_0-9] 单词字符(字母数字下划线)
\W [^a-zA-Z_0-9] 非单词字符
5. 边界
  • 正则表达式还提供了几个常用的边界匹配字符,如下表:
字符 含义
^ 以xxx开始
$ 以xxx结束
\b 单词边界
\B 非单词边界
6. 量词
字符 含义
? 出现一次或0次(最多出现一次)
+ 出现一次或多次(最少出现一次)
* 出现0次或多次(任意次)
{n} 出现n次
{n, m} 出现n到m次
{n, } 至少出现n次

js正则的贪婪模式和非贪婪模式

  1. 贪婪模式:\d{3,6}匹配数字3到6次,那么在匹配的过程中会尽可能多的的匹配。如:
 '12345678'.replace(/\d{3,6}/, 'X')  // "X78"
  1. 非贪婪模式:在量词后面加上 ? ,可以让正则表达式尽可能少的匹配,也就是非贪婪模式。
  '12345678'.replace(/\d{3,6}?/, 'X')  // "X45678"

分组

对一组正则规则加(),可以对正则进行分组,

比如:对taoyouyou这组单词匹配三次
taoyouyou{3}

可视化后能看到只是最后一个单词u重复了3次,并没有达到目的
(taoyouyou){3}

taoyouyou重复了3次

捕获组引用

对字符串进行分组后,

捕获组的编号是按照“(”出现的顺序,从左到右,从1开始进行编号的 。

在replace方法中可以通过$number(number表示捕获组的编号)进行引用

在js表达式中分组引用通过RegExp对象暴露出来,

即通过RegExp.$number引用

例如实现2019-02-22 => 02/22/2019的转化。

'2019-02-22'.replace(/(\d{4})-(\d{2})-(\d{2})/,'$2/$3/$1')   //  "02/22/2019"

反向引用

正则表达式中所定义的捕获的反向引用指的是

将捕获作为正则表达式中能够成功匹配术语时的候选字符串

这种术语表示法是在反斜杆后面加一个要引用的捕获数量。
捕获组捕获到的内容,不仅可以在正则表达式外部通过程序进行引用,也可以在正则表达式内部进行引用,这种引用方式就是反向引用。反向引用的作用通常是用来查找或限定重复、查找或限定指定标识配对出现等等。
例如:([a-z])\1{2}也就表达连续三个相同的小写字母。

忽略分组

不需要捕获某些分组只需要在分组内加 ?: 就可以了。
(?:taoyouyou).(ok)

前瞻

  • 正则表达式从文本头部向尾部解析,文本尾部方向称为 “前”
  • 前瞻:正则表达式匹配到规则的时候,向前检查是否符合断言,后顾/后瞻方向相反
  • JavaScript不支持后顾
  • 符合和不符合某项断言称为肯定/正向匹配和否定/负向匹配
名称 正则 含义
正向前瞻 exp(?=assert)
负向前瞻 exp(?!assert)
正向后顾 exp(?<=assert) JavaScript不支持
负向后顾 exp(?<!assert) JavaScript不支持

正则对象属性

  • global: 是否全文匹配,默认false,匹配规则后面加g开启
  • ignoreCase: 是否大小写敏感, 默认false,匹配规则后面加i开启
  • multiline: 多行搜索, 默认false,匹配规则后面加m开启
  • lastIndex: 当前表达式匹配内容最后一个字符的下一个位置
  • source: 正则表达式的文本字符串
    这几个属性都是只读属性
reg=/\d/gmi
reg.global  // true
reg.ignoreCase  // true
reg.multiline  // true
reg.lastIndex  // 0
reg.source  // "\d"

对象方法

1. 正则test方法

RegExp.prototype.test(str)

  • 用于测试字符串参数中是否存在匹配正则表达式模式的字符串
  • 如果存在则返回true,否则返回false

需要注意的是:

var reg2=/\w/g;

进行reg2.test(‘ab’)时,第三次会变成false
原因:

while(reg2.test('ab')){
  console.log(reg2.lastIndex);
}

即进行test时,正则表达式的lastIndex会发生变化
输出:1 2 表示第一个匹配上了、第二个匹配上了

2. 正则exec方法

RegExp.prototype.exec(str)

  • 使用正则表达式模式对字符串参数进行匹配搜索,并将更新全局对象属性以反映匹配结果
  • 如果没有匹配则返回null,否则返回一个结果数组:
    属性index(声明匹配文本的第一个字符的位置), input(存放被检索的字符串string)
    数组本身: [与正则表达式相匹配的文本,与正则对象的第一个子表达式(分组)相匹配的文本, 与正则对象的第二个子表达式(分组)相匹配的文本,…… ]
var reg3=/\d(\w)(\w)\d/;    // 非全局搜索只能匹配到第一个结果
var reg4=/\d(\w)(\w)\d/g;   // 全局搜索匹配全文
var ts = '$1az2bb3cy4dd5ee'
var ret1= reg3.exec(ts)     // (3) ["1az2", "a", "z", index: 1, input: "$1az2bb3cy4dd5ee", groups: undefined]
console.log(reg3.lastIndex+'\t'+ret1.index+'\t'+ret1.toString())
// "0 1 1az2, a, z"
while(ret2 = reg4.exec(ts)){
  console.log(reg4.lastIndex+'\t'+ret2.index+'\t'+ret2.toString())
}
// "5 1 1az2, a, z"
// "11 7 1az2, a, z"

3. 字符串对象search方法

String.prototype.search(reg)

  • search方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串
  • 返回第一个匹配结果index,查不到返回-1
  • search()不执行全局匹配,它将忽略标志g, 并总是从字符串的开始进行检索

4. 字符串对象match方法

String.prototype.match(reg)

  • match方法将检索字符串,以找到一个或多个与regexp相匹配的字符串
  • regexp是否具有标志g对结果影响很大
非全局调用
  • 返回数组的第一个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本
  • 除了常规的数组元素之外,返回的数组还含有2个对象属性:index声明匹配文本的起始字符在字符串的位置,input声明对stringObject的引用
  • 全局调用的返回与正则表达式的exec方法相类似,即:[与正则表达式相匹配的文本,与正则对象的第一个子表达式(分组)相匹配的文本, 与正则对象的第二个子表达式(分组)相匹配的文本,…… ],数组有index和input属性
全局调用
  • 如果regexp具有标志g则match方法将执行全局检索,找到字符串中的所有匹配子字符串
  • 没有找到任何匹配的子串,则返回null
  • 如果找到了一个或多个匹配子串,则返回-一个数组
  • 数组元素中存放的是字符串中所有的匹配子串,而且也没有index属性或input属性
var reg3=/\d(\w)(\w)\d/;    // 非全局搜索只能匹配到第一个结果
var reg4=/\d(\w)(\w)\d/g;   // 全局搜索匹配全文
var ts = '$1az2bb3cy4dd5ee'
var ret1= ts.match(reg3)   // (3) ["1az2", "a", "z", index: 1, input: "$1az2bb3cy4dd5ee", groups: undefined]   
console.log(reg3.lastIndex+'\t'+ret1.index)  // 0, 1
var ret2 = ts.match(reg4)  // (2) ["1az2", "3cy4"]
console.log(reg4.lastIndex+'\t'+ret2.index)   // 0 undefined

5. 字符串对象split方法

String.prototype.split(reg)

  • 我们经常使用split方法把字符串分割为字符数组
    eg: ‘a,b,c,d’.split(’,’); //[“a”, “b”, “c”, “d”]
  • 在一些复杂的分割情况下我们可以使用正则表达式解决
    eg: ‘alb2c3d’.split(/\d/; //[“a”, “b”, “c”, “d”]

6. 字符串对象replace方法

String.prototype.replace

  • String.prototype.replace(str, replaceStr)
  • String.prototype.replace(reg, replaceStr)
  • String.prototype.replace(reg, function)
    function参数含义
    function会在每次匹配替换的时候调用,有四个参数
    1、匹配字符串
    2、正则表达式分组内容,没有分组则没有该参数
    3、匹配项在字符串中的index
    4、原字符串
'a1b2c3d4e5'.replace(/\d/g, function(match, index, origin){
  return parseInt(match) + 1;
})  // "a2b3c4d5e6"

'a1b2c3d4e5'.replace(/(\d)(\w)(\d)/g, function(match, group1, group2, group3, index, origin){
  console.log(match)
  return group1 + group3;
})  
// 1b2
// 3d4
// "a12c34e5"

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消