正则表达式

1. 表达式结构和声明

正则表达式基本结构包括两部分,匹配规则和标识符,其中匹配规则包含在两个‘/’中,标识符在最后,形如/匹配规则/标识符

标识符包括

  1. g:global,全局
  2. i:ignoreCase,大小写不敏感
  3. m:multiline,匹配多行

对象声明方式有两种:

  1. 字面量,即let reg = /[abc]/i
  2. 通过new关键字,如let reg = new RegExp('[abc]','i'),构造函数包括两个参数,参数一字符串表达式(有些字符需要转义,如双引号''等),参数而为标识符

对象属性

  1. globalignoreCasemultiline分别对应标识符g i m使用状况
  2. lastIndex:下一次exec匹配开始的索引
  3. source:正则表达式源码文本

方法

  1. test:测试,返回boolean类型
  2. exec:执行,返回匹配的字符串及分组

2. 表达式因子

所谓的匹配规则,便是有限个表达式因子的规则组合,组合规则当然要根据其功能来决定。主要的表达式因子及其含义如下,式中...代表需要填入的内容:

  • /:前面提到的表达式结构的一部分,由两个/包含规则便创建了一个字面量正则表达式,如/ab/i,匹配带有ab的字符串
  • \:转义符,相信有编程基础的都应该知道,想要吧一些特殊字符当做字符串处理或者表示一些字符串不能表示的字符需要转义,如/\/a/,匹配带有/a的字符串,而/n表示换行符等。RegExp中需要转移的因子大致有\/[](){}?+-*|.^$
  • ^:当在匹配规则前面使用时表示开始符,如/^[ac]/,表示匹配以a或c开始的字符串;但当在一个字符类前面使用时表示取反,如/^[^ac]/表示
// RegExp
// 执行函数
function exec(reg,str){
    let result = reg.exec(str)
    console.log(result)
    return result
}
// 匹配以a开始的字符串
let reg = /^[a]/,
str1 = 'and',
str2 = 'but'
exec(reg,str1) // [ 'a', index: 0, input: 'acnd' ]
exec(reg,str2) // null
// 取反 匹配不以a开始的字符串
reg = /^[^a]/
exec(reg,str1) // null
exec(reg,str2) // [ 'b', index: 0, input: 'but' ]
  • $:结束符,显而易见,就是匹配以某种形式结束的字符串
// 匹配不以结尾的字符串
reg = /[^t]$/
exec(reg,str1) // [ 'd', index: 2, input: 'and' ]
exec(reg,str2) // null

-: 表示范围,如[a-z],表示小写字母az;当需要表示-是需要转义,即/\-/,即匹配含有-的字符串

+: 表示必选,需要匹配的字符至少出现一次,也可表示为{1,}

?: 表示可选,需要匹配的字符出现0或1次,也可表示为{0,1}

*: 表示可有可无,需要匹配的字符出现0或多次,也可表示为{0,}

[...]:方括号表示一个字符类,如[abc][123]或者[abc123],类似于编程里面的Class

{m,n}:花括号表示对匹配字符出现次数的限定,其中m为最少出现次数,n为最多出现次数;当出现次数最多没有限制的时候,n可空缺

(...):圆括号括起来的内容表示一个捕获分组

(?:...):圆括号内以?:开始的为非捕获分组,不会返回字符串分组,效率更高

// 下边来比较一下捕获分组与不可捕获分组的区别
let str  = 'Hello World!',
// 捕获分组
reg = /([a-z]+)\s([a-z]+)/i
let resultAry =exec(reg,str) // [ 'Hello World',  'Hello',  'World',  index: 0,  input: 'Hello World!' ]

// 插一句 获取分组结果的方式
// 方式1 数组索引 o为原始字符串,后续为分组
console.log(resultAry[1]) // 分组1:'Hello’
console.log(resultAry[2])  // 分组2:'World’

// 方式2 通过RegExp对象的'$索引'来获取,索引范围'1-9';但是这种方式需要在'reg.exec或test'后立即调用,或者字符串的`match、search、split`等方法
console.log(RegExp.$1) // 分组1:'input’
console.log(RegExp.$2)  // 分组2:'’
// 像这样才有效
reg.exec(str) 
console.log(RegExp.$1) // 分组1:'Hello’
console.log(RegExp.$2)  // 分组2:'World’

// 非捕获分组
reg = /(?:[a-z]+)\s(?:[a-z]+)/i
exec(reg,str) // [ 'Hello World', index: 0, input: 'Hello World!' ]
// 通过比较我们发现,非捕获分组确实不会返回字符串分组,即['Hello','World']
  • .: 匹配除换行符 \n 之外的任何单字符
  • \d: 匹配数字,相当于[0-9]
  • \s: 匹配空白(whitespace)

3. 举例分析:邮箱地址匹配

假设邮箱验证规则为:只允许英文字母、数字、下划线、英文句号、以及中划线组成,且下划线和中划线不得出现在开始和结束位置,区分大小写。

let reg = /^([a-zA-Z0-9]+[_\-]?[a-zA-Z0-9]+)@([a-zA-Z0-9]+[_\-]?[a-zA-Z0-9]+).([a-zA-Z]+)$/;

let email1 = '@hsajdh.com',
    email2 = '_dnaksl_@.com',
    email3 = '-absdbn_dnaksl_@hsajdh.',
    email4 = 'absdbn_dnaksl@hsajdh.com_',
    email5 = 'absdbn_dnaksl@hsajdh.com'

console.log(reg.test(email1)) // false
console.log(reg.test(email2)) // false
console.log(reg.test(email3)) // false
console.log(reg.test(email4)) // false
console.log(reg.test(email5)) // true

解释说明

  1. 首先邮箱地址分为三组,名称@域名.后缀(假设只有一级域名),得到/^()@().()$/
  2. 名称由字母、数字、下划线和中划线组成,且下划线和中划线必须出现在中间(可选,记得中划线要转义),得到([a-zA-Z0-9]+[_\-]?[a-zA-Z0-9])+
  3. 域名和名称规则一样,也为([a-zA-Z0-9]+[_\-]?[a-zA-Z0-9])+
  4. 后缀只能回字母,得到[[a-zA-Z]+]
  5. 最终的表达式为/^([a-zA-Z0-9]+[_\-]?[a-zA-Z0-9]+)@([a-zA-Z0-9]+[_\-]?[a-zA-Z0-9]+).([a-zA-Z]+)$/