正则表达式是匹配模式,要么匹配字符, 要么匹配位置;匹配位置实际上可以理解为匹配空字符
两种模糊匹配
- 横向模糊匹配
横向模糊匹配,一个正则可匹配的字符串的长度不是固定的,可以是多种情况;
其实现方式:使用量词;
- 纵向模糊匹配
纵向模糊匹配,一个正则匹配的字符串,具体到某一位字符时,它可以不是确定的某个字符,可以有多种可能
其实现方式:使用字符组
量词:强调的是字符出现的次数,如某个字符最少出现次数,最多出现次数
字符组:字符类,强调的是匹配字符范围
具体内容见思维导图:
案例分析
案例一:匹配 16 进制颜色值
/// 要求匹配:
/// #ffbbad
/// #Fc01DF
/// #FFF
/// #ffE
/// 分析:
/// 表示一个 16 进制字符,可以用字符组 [0-9a-fA-F]
/// 其中字符可以出现 3次 或 6次,这里需要用到量词和多选分支
/// 注意,使用多选分支时,需要注意顺序
let regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g
let string = '#ffbbad #Fc01df #Fc01DF #FFF #ffE'
console.log( string.match(regex) )
// => [ '#ffbbad', '#Fc01df', '#Fc01DF', '#FFF', '#ffE' ]
案例二:匹配时间
/// 以 24 小时制为例
/// 要求匹配:
/// 23:59
/// 02:07
/// 分析:
/// 共四位数字,第一位数字可以为 [0-2]
/// 当第一位为 2 的时候,第二位可以为 [0-3],其他情况,第二位 [0-9]
/// 第三位 [0-5], 第四位 [0-9]
let regex = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/
console.log( regex.test('23:59') ) // => true
console.log( regex.test('02:07') ) // => true
console.log( regex.test('24:00') ) // => false
/// 备注:正则中使用了 ^ 和 $ ,分别表示字符串开头和结尾
/// 如果要求能够匹配 “7:9”,也就是前面的 0 可以省略
regex = /^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/
console.log( regex.test('23:59') ) // => true
console.log( regex.test('02:07') ) // => true
console.log( regex.test('7:9') ) // => true
console.log( regex.test('7:09') ) // => true
案例三:匹配日期
/// 以日期格式 yyyy-mm-dd 为例
/// 要求匹配:
/// 2021-01-05
/// 分析:
/// 1. 年数可以是 4位数字,[0-9]{4}, 再给个额外的要求,要求配 20xx 开头的年份,则 20[0-9]{2}
/// 2. 月数,共 12 个月,分两种情况,01 ... 09 和 10 ... 12 ,可以用 (0[1-9]|1[0-2])
/// 3. 日数,最大 31 天,可以用 (0[1-9]|[12][0-9]|3[01])
let regex = /^20[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/
console.log( regex.test('2021-01-05') ) // => true
console.log( regex.test('2099-01-01') ) // => true
console.log( regex.test('1999-01-31') ) // => false
/// 备注:正则中使用了 ^ 和 $ ,分别表示字符串开头和结尾
案例四:匹配 windows 操作系统文件路径
/// 要求匹配:
/// D:\wakacodes\front-end\regular-expression\regular expression.pdf
/// D:\wakacodes\front-end\regular-expression\
/// D:\wakacodes\front-end
/// D:\
/// 分析:
/// 整体模式是:
/// 盘符:\文件夹\文件夹\文件夹\文件
/// 其中匹配 'D:\',需要使用 [a-zA-Z]:\\,盘符不区分大小写,\ 需要转义
/// 文件名或文件夹名不能包含一些特殊字符,可以排除字符组 [^\\:*<>|"?\r\n]
/// 文件名或文件夹名至少包含一个字符,需要使用量词 + ,因此匹配 "文件夹\" 可以使用 [^\\:*<>|"?\r\n]+\\
/// 文件夹出现的次数,任意次,([^\\:*<>|"?\r\n]+\\)* ,括号表示其内部是一个整体
/// 路径最后一部分可以是文件夹但没有 \ ,因此需要添加 ([^\\:*<>|"?\r\n]+)?
let regex = /^[a-zA-Z]:\\([^\\:*<>|"?\r\n]+\\)*([^\\:*<>|"?\r\n]+)?$/
console.log( regex.test('D:\\wakacodes\\front-end\\regular-expression\\regular expression.pdf') ) // => true
console.log( regex.test('D:\\wakacodes\\front-end\\regular-expression\\') ) // => true
console.log( regex.test('D:\\wakacodes\\front-end') ) // => true
console.log( regex.test('D:\\') ) // => true
/// 备注:正则中使用了 ^ 和 $ ,分别表示字符串开头和结尾
案例五:匹配 DOM 元素的 id 属性内容
/// 要求匹配:
/// <div id="container" class="main"></div>
/// 提取 id="container"
/// 分析:
/// 因为要提取 id="container",最直接的想法就是 /id=".*"/ ,使用 . 通配符
let regex = /id=".*"/
let string = '<div id="container" class="main"></div>'
console.log( string.match(regex)[0] )
// => id="container" class="main"
/// 发现结果不是想要的,但接近了
/// 因为通配符 . 本身就能匹配到双引号,而量词 * 又是贪婪的,可能出现任意次数,所以会一直匹配到最后一个双引号为止
/// 那么,太贪婪了,就给它变得不贪婪,那就懒惰一下
/// 使用惰性匹配
regex = /id=".*?"/
console.log( string.match(regex)[0] )
// => id="container"
/// 但这种模式效率比较低,因为 贪婪或惰性匹配的时候,会涉及到”回溯“
/// 优化如下:
regex = /id="[^"]*"/
console.log( string.match(regex)[0] )
// => id="container"