吃透js中正则表达式

我们在开发的过程中总会碰到一些需求需要做字符串匹配,当遇到一些稍微复杂一点的匹配规则时,如果我们对正则还不那么清晰,我们总是会去网上搜索一些现成的正则匹配,ctrl+c,ctrl+v。时间长了我们对这种修修补补的方式不厌其烦。那么今天就对正则表达式做足功夫,磨刀不误砍柴工,首先推荐一个学习正则的一个可视化的工具网址: 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次

反向引用

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

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

忽略分组

不需要捕获某些分组只需要在分组内加 ?: 就可以了。 (?: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"

复制代码

原文地址:taoyouyou.gitee.io

猜你喜欢

转载自juejin.im/post/5c6fefc65188256559172f46