快速理解正则,并提高代码编写质量

170 阅读3分钟

1) 正则创建的两种形式

var reg1 = /1/; //字面量形式 把内容写到双斜杆(//)里面
var reg2 = new RegExp('1');//构造函数
reg1.test("123");//true
reg2.test("123");//true

2) 特殊元字符:

符号含义
\d数字
\D除了数字以外的其他字符
\w数字、字母或者下划线"_"
\W除(数字、字母或者下划线"_")其他字符
以什么开头,用在正则开头
$以什么结尾,用在正则最后
[abc]a或者b或者c
[^ab]除ab以外其他的字
[a-z]小写字母
[A-Z]小写字母
[a-zA-Z]所有字母
a|b|ca或者b或者c
.除了换行符以外其他的字符,若再[]里面就代表"."字符串
( )提升优先级
/^11|12$/以11开头或者以12结尾的字符串
/^(11|12)$/要不11 要不12

3) 量(数量)词元素,用在修饰的字符后面

元素含义
?/\d?/ 代表前边的字符出现0或1次
+代表前边的字符出现1到多次
*代表前边的字符出现0到多次
{n}代表前边的字符出现n次
{n,}代表前边的字符出现n到多次
{n,m}代表前边的字符出现n到m次
([ab])\1反向引用,匹配aa或者bb
([ab])\1+反向引用,匹配连续多个a或者多个b
(?:pattern)匹配检验 windows(?:2000|NT|98) 等同于 windows2000|windowsNT|windows98
(?=pattern)正向肯定预查
(?!pattern)正向否定预查

4) 修饰符

字符原单词含义
gglobal全局匹配,最多用于捕获,匹配时很少有
iignoreCase忽略大小写
mmultiline多行匹配

5) 使用方法介绍

方法描述
test在字符串中查找匹配的RegExp方法,匹配到返回true(未匹配到返回false)
exec在字符串中查找匹配的RegExp方法,匹配到返回一个数组(未匹配到返回null)
match在字符串中查找匹配的String方法,匹配到返回一个数组(未匹配到返回null)
matchAll在字符串中查找所有匹配的String方法,匹配到返回一个迭代器)
replace在字符串中查找所有匹配的String方法,并且使用替换掉匹配的字符串)

6) 使用方法案例

6.1. test()

//检验有效数字,数字前可以有-+号,小数的话,点后面必须有值,

var  reg = /^[+\-]?(\d|[1-9]\d+)(\.\d+)?$/;
var a='01',b='0.',c='+24342',d='-+123';
console.log(reg.test(a),reg.test(b),reg.test(c),reg.test(d));
//=>false false true false

//检验密码必须包含数字、大写字母、小写字母并且位数在8-15

//(?=.*[A-Z])使用正向预查,前面可以有或者没有任意字符,必须有一个大写字母
reg = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,15}$/;
reg.test("AAaa23412")//true
6.2. exec() 捕获(查询)返回一个数组
// 捕获的贪婪性  一次获取 会尽可能多的去获取内容 想解决这个贪 就在量词后边 加一个?
// 捕获的懒惰行  只获取一次;解决这个懒 使用修饰符 g
var str = 'asda2009cccc2022,as,123dd22';
var reg = /\d+/
console.log(reg.exec(str));//['2009', index: 4, input: 'asda2009cccc2022,as,123dd22', groups: undefined]
//匹配数字,只能找到第一个2009符合,找到后就立马停止,再次执行仍然是2009
console.log(reg.exec(str));//['2009', index: 4, input: 'asda2009cccc2022,as,123dd22', groups: undefined]
// 加上g全局查找效果如何呢?
 reg = /\d+/g;
console.log(reg.lastIndex);//0 lastIndex 代表的就是当次匹配对应字符串的开始索引0
console.log(reg.exec(str));//['2009', index: 4, input: 'asda2009cccc2022,as,123dd22', groups: undefined]
console.log(reg.lastIndex);//8 这次再执查找的索引为8,也就是从上次查找内容的最后的索引后面开始的
console.log(reg.exec(str));//['2022', index: 12, input: 'asda2009cccc2022,as,123dd22', groups: undefined]
6.2.1拓展通过exec编写一个execAll方法
str = 'asda2009cccc2022,as,123dd22';
reg = /\d+/g;
RegExp.prototype.execAll = function (str) {
    if (!this.global) {
      throw new Error("没加g")
    }
    this.lastIndex = 0; // 防止这个正则的lastIndex不是0而导致获取到的内容不对
    let ary = [];
    let item = this.exec(str)
    while (item) {
      ary.push(item[0])
      item = this.exec(str)
    }
    return ary
}
console.log(reg.execAll(str)); //['2009', '2022', '123', '22']
console.log(reg.execAll(str));//再次运行结果一样 ['2009', '2022', '123', '22']
6.2.2 ()的作用

之前()描述过就是一个特殊元字符,提升优先级用来表示一个整体,现在把它在捕获方法里使用后,()也代表一个小组,在整个正则表达式中,遇到一个()就会执行一次捕获,捕获的结果会统一返回到结果的数组里面

reg = /^\d{6}(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/
str = '130425199110134526'
res1 = reg.exec(str)// ['130425199110134526', '1991', '10', '13', '2', '6', index: 0, input: '130425199110134526', groups: undefined]
//分析结果可以看到,数组从0-n项依次式每次捕获的字符串
//res[0] =>'130425199110134526'//全局捕获对应的结果
//res[1] =>'1991'//里面(\d{4})项 捕获对应的结果
//......
//res[5] =>'6'//里面(\d|X)项 捕获对应的结果
6.3.字符串的match

match是string类型的方法,可以利用正则匹配完成捕获功能,类似RegExp的exec方法

6.3.1 功能对比RegExp.exec
reg = /^\d{6}(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/
str = '130425199110134526'
res1 = reg.exec(str)
res2 = str.match(reg)
console.log(res1, res2)
//['130425199110134526', '1991', '10', '13', '2', '6', index: 0, input: '130425199110134526', groups: undefined]
//['130425199110134526', '1991', '10', '13', '2', '6', index: 0, input: '130425199110134526', groups: undefined]
//运行结果一样

那么我们把正则表达式加上g呢?再看下结果

reg = /^\d{6}(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/g
res1 = reg.exec(str)
res2 = str.match(reg)
console.log(res1, res2)
//['130425199110134526', '1991', '10', '13', '2', '6', index: 0, input: '130425199110134526', groups: undefined]
//['130425199110134526']
//很明显macth只能匹配一次全局的,不会理会()小分组的内容
6.4.字符串的replace

String.prototype.replace()

str.replace(regexp|substr, newSubStr|function)

这个方法丝毫不陌生,搭配上正则使用后,功能拓展更大

'1-2-3-4'.replace('-','');//'12-3-4'
'1-2-3-4'.replace(/-/,'');//'12-3-4'
'1-2-3-4'.replace(/-/g,'');//'1234'
'1-2-3-4'.replace(/-/g,a=>'')//'1234'

7) 拓展面试题应用

很多功能可能会通过字符串方法,数组方法解决,如果通过正则方式解决会提高B格,让面试官对你高看一些

7.1. 简单实现获取url参数对象
//定义一个比较简单的url,暂时不考虑特殊清楚比如字符包括关键字、name重复等问题
let str = 'https://www.baidu.com/s?name=xiaom&age=30&id=0xc1'
function queryURL(str, key) {
  let reg = /([^?&]+)=([^&]+)/g //name=xiaom 前面(不是?或者&)中间是= 不以&结尾 的字符串部分
  let obj = {};
  str.replace(reg, function (a, b, c) {//a:name=xiao,b:name,c:xiaom
    obj[b] = c
  })
  return key ? obj[key] : obj
}
console.log(queryURL(str)); //{name: 'xiaom', age: '30', id: '0xc1'}
7.2. 驼峰命名 get-element-by-id ---> getElementById
let str2 = 'get-element-by-id'
let res = str2.replace(/-(\w)/g, function (a, b) {
  return b.toUpperCase()   //函数的返回结果 是把大正则匹配的内容给替换了
})
console.log(res);//getElementById
7.3. 字符串里面大小写反转 AA1a--->aa1A
let str3 = 'DFGDFdfgeSRgDSFGdfGERdgDFgAERddeRE3453函数i职能划分'
let res3 =  str3.replace(/[a-zA-Z]/g, function ($0) {
  if ($0.toLowerCase() === $0) {//'A'!='A'.toLowerCase() ,'a'=='a'.toLowerCase() 
    return $0.toUpperCase()
  } else {
    return $0.toLowerCase()
  }
})
7.4. 查找一个字符串中出现次数最多的字符
let str = 'sdfgsdge4rtsgfsdfgsrthhcxvhstgsfgsf',
    str2 = str.split('').sort().join('')//4cdddefffffgggggghhhrrsssssssstttvx
let maxNum = 0, maxStr = '';
str2.replace(/(\w)\1+/g, function ($0) {
  if ($0.length > maxNum) {
    maxNum = $0.length
    maxStr = $0
  }
})
console.log(maxNum, maxStr)//8 'ssssssss'
7.5. 按照模板输出对象内容
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
function render(template, data) {
  let res = template.replace(/\{\{(\w+)\}\}/g, function (a, b) {
    return data[b]//b---> {{name}}
  })
  return res
}
console.log(render(template, data))// '我是姓名,年龄18,性别undefined'
7.6. 千分符
let str = '12345666.048586' //12,345,666.048586
function qian(str) {
//出现,最少千分位1111,匹配4位数
//匹配某个数字\d 后面跟着至少1个(三位数)且结尾 
  let reg = /\d(?=(\d{3})+$)/g
  let xiaoshu = str.split('.')[1]
  str = str.split('.')[0]
  return str.replace(reg, function (a) {
    return a + ','//匹配到次数然后给他加给“,”
  }) + (xiaoshu ? '.' + xiaoshu : '')
}
console.log(qian(str))// '12,345,666.048586'

如果没有小数可以简写

'1233454899992'.replace(/\d(?=(\d{3})+$)/g,'$&,');//'1,233,454,899,992'
// $& 插入匹配字符串