JavaScript 深入正则表达式
正则表达式是一个专门用来检测 字符串 是否符合规则的表达式,大部分语言都能有用到,作为前端程序员,在一般业务中使用的都不多,但至少要了解其基本特性,常用的表达式就不在这里列举了,百度一搜一大把的~
创建方法
-
-
字面量方式创建
- 语法:
var reg = /abcd/
意义:检测的字符串内必须包含一个 'abcd' 字符串片段
- 语法:
-
内置构造函数方式创建
-
语法:
var reg = new RegExp('abcd')
可以省略new 括号里可传两个参数('abcd', 'gi') i或g写在后面
-
-
-
两种表达式的区别
-
字面量方式不接受字符串拼接
内置构造函数: 可以拼接字符串, 因为第一个参数就是以字符串的形式书写正则内容
什么是拼接字符串?
var a = [ 'HH', 'NN', 'MM' ] a.join('|') => HH|MM|NN var reg2 = new RegExp('(' + a.join('|') + ')') console.log(reg2)
- 书写基本元字符
- 字面量方式 直接书写 \s\d\w
- 内置构造函数 需要书写 \\s\\d\\w
字符串 + 有一个特殊符号叫做 转义符号(\) + 可以吧有意义的转化成没有意义的文本, 把没有意义的文本转换成有意义的符号 => 例子: \n => n 本身是没有意义的文本 => \n 表示换行的意思 + 当你在字符串内书写 \s => 会把没有意义的 s 文本转换成有意义的符号 => 但是字符串内没有 \s 这个符号 => 其实就是还是 s 文本 正则 + 第一个参数一字符串的形式书写正则内的内容 + 你书写的是 '\s\d\w', 因为在字符串内确实没有 \s\d\w 这三个符号, 所以其实就是 sdw 文本 + 当你书写 '\\s' 的时候 => 第一个 \ 是转义符号, 把第二个 \ 给转换了 => 转换成了一个没有意义的 \ 文本 + 你给出的 '\\s\\d\\w' 其实才是一段合法的元字符文本
-
常用方法
-
-
匹配方法
- 语法: 正则.test(字符串)
- 返回值: 布尔值, 有就是true 没有就是false
- 用一个正则表达式不要连用 test
let reg = /a/g // 不加g 没影响 console.log(reg.test('fasdg')) // true console.log(reg.test('fsasdg')) // 当a在前两位的时候是 false 第三位开始才是 ture
-
-
捕获方法
从一段完整字符串中获取出一部分满足正则表达式的内容片段
-
语法: 正则.exec(字符串)
-
返回值:
如果原始字符串中没有符合的片段,就是null
如果原始字符串中有符合的片段,那么就是一个数组
2-1 没有() 也没有全局标识g
[0] 就是捕获出来的第一个片段,有且仅有第一个满足规则的片段
2-2 有全局标识g
[0] 就是捕获出来的第一个片段
第二次捕获的时候会从第一次捕获结束的位置开始检索
每一次捕获返回的[0]都是新的片段,直到捕获不到内容为止,返回null后再下一次捕获又从字符串开始位置检索
2-3 有()
在返回值数组中,从[1]开始,依次是每一个()的单独内容
-
匹配但不捕获
-
语法: (?:) 写在冒号后面
这种就是需要用到括号,但是还不想捕获括号里面的内容的时候要用
比如(?:\d|x) 这种需要或的情况的时候
-
-
-
标识符
用来修饰整个正则表达式的
-
标识符位置
- 字面量方式: var reg = /abcd/(写在这里,顺序任意)
- 内置构造函数方式: var reg = new RegExp('abcd', 'gi') 第二个参数
-
标识符种类
- i 忽略大小写
- g 全局匹配,多用于重复执行
- m 换行匹配 => 如果匹配字符串遇到换行,那么重新开始计算行首
例如/^\d/gm 替换的话如果有多行,是替换每一行的第一个数字
元字符
填写在正则表达式内的规则符号
-
基本元字符
- \d 表示 一位 数字
- \D 表示 一位 非数字
- \s 表示 一位 空白内容(空格,缩进,换行...)
- \S 表示 一位 非空白内容
- \w 表示 一位 数字(0-9)字母(a-zA-Z)下划线(_)都行
- \W 表示 一位 非数字字母下划线
- . 表示 一位 非换行以外的任意内容
- \ 转义符,把有意义的符号转化成没有意义的文本,把没有意义的文本转换成有意义的符号
-
重复元字符
\n 表示重复出现第n个小括号的内容,必须和之前出现的一样
\1 表示在 \1 位置重复出现一次第一个小阔内的内容内容而且必须和 第一个小括号出现的内容一模一样
\1* 表示重复第一个括号里面的内容至少重复1次 \1+ 表示至少重复两个 使用\1 这种的方法 前面的东西必须放在群组里 ([a-z])\1*
-
边界符
-
- ^ 表示字符串开头
- $ 表示字符串结尾
注意: 当 开头和结尾 一起使用的时候, 表示从开头到结尾
var emailX = /^[^_]\w{4,11}@(qq|163|sina)\.(com|cn)$/ =>邮箱验证,开头到结尾有5-12个\w组成
-
-
限定符
用来修饰出现多少次的符号 注意: 一个限定符只能修饰前面一个符号
-
* 表示0~多次(包含0)
-
+ 表示1~多次
-
? 表示0~1次(没有或只有1次)
-
{n} 表示n次
=> {0,} 等价于 * , {1,} 等价于 +
注意点:想要使用这个,正则表达式中必须含义结尾,否则大于n的次数也是可以测试通过的,只不过捕获的时候还是捕获n个
{0} 匹配空字符,不论前面有什么,都匹配的是空, 如'abc' 匹配到 4个空字符串
-
{n,m} 表示n~m次
=> {0,1} 等价于 ?
-
-
特殊符号
-
( ) 组
含义1: 表示一个整体
含义2: 表示单独捕获
-
| | |中间如果没写,查的是空字符, 两侧任意一边没有写,查的是空字符
表示左边或者右边的任意一个都行
let reg = /^18|29$/ 匹配的是:以18开头或者以29结尾的都可以 let reg = /^(18|29)$/ 匹配的是:18或者29中的一个 只能出现一次
-
[ ]
表示 [] 内的任意一个都行 , 相同字符无意义 如[aba] 只匹配a或b
. 在中括号内是字符点, 不再是通配符 中括号内再匹配 [] () {} 时需要 \ 转义
-
[^]
表示 [^] 内的任意一个都不行
以上两个中括号都是只占一个字符位置,括号内可以写多个字符
-
-
表示从一个字符到一个字符, 必须要 ASCII 编码连着的 和 [] 或者 [^] 连用
- [0-9] 等价于 \d [0-9a-zA-Z_] 等价于 \w [ ^0-9] 等价于 \D [^0-9a-zA-Z_] 等价于 \W
-
断言和重复筛选
-
重复筛选
- ( )\1+ ( )\1* 表示括号里面的内容需要重复最大的次数 贪婪匹配 1+ 至少有两次 1* 至少有一次
let str = 'asdfsdfsadfsafdsagwhhtrhgdvg' function strTimes(str){ return str.split('').sort().join('').replace(/(a-zA-Z)\1*/g,function(item){ return item[0] + '{' + item.length + '}' }) }
-
断言
断言语句不占位,且不会出现在返回值当中, 只是判定前后是否存在
- 后置肯定断言
- 语法: ?= 表示前面的内容后面必须包含后面这个内容 存在则匹配前面的内容
- 后置否定断言 语法: ?!
- 前置肯定断言 语法: ?<= 判定后面的内容前面是否存在这个内容 存在则匹配后面的内容
- 前置否定断言 语法: ?<!
console.log("《西游记》,《三国演义》,《水浒》,《红楼梦》".match(/(?<=《).*?(?=》)/g)) 返回不包含《 》组成的数组
- 后置肯定断言
两大特性
-
-
懒惰
每当使用正则去捕获字符串的时候,每次都是从开始位置检索,一般只返回一个值
- 解决: 使用全局标识符g
-
贪婪
在使用了区间写法的时候,在捕获的时候会尽可能多的去捕获内容,也就是捕获满足条件的最长组合,这种就是默认的贪婪匹配
-
解决: 非贪婪匹配(捕获满足条件最小长度) 在限定符后面加上?
.*? .+? 这两个前后都必须有条件 如 a.*?b 有起始和结束
?? ===== {0,2}
{n,}? {n,m}?
-
-
和字符串的组合方法
- replace()
- 语法: 字符串.replace(正则表达式,换上字符),字符串.replace(正则表达式,函数)
- 这里的函数(参数1,参数2,...,参数3,参数4) 参数1 是每次找到的内容,参数二是使用()后筛选后的内容,倒数第二个参数是下标,最后一个参数是原字符串, 函数可以简写'1∗∗∗∗1****1∗∗∗∗2'$1 就是群组1 表示原本第几个() 里面的内容是啥 返回的就是啥
- 返回值: 不填写全局g替换一个,填写g有多少替换多少
用此方法替换关键字:
var str = 'fXXfdHHsMMgNNfHHdNNhHHgfsMMdsNNgsdb'
var a = [ 'HH', 'NN', 'MM', 'XX' ]
var r3 = str.replace(new RegExp('(' + a.join('|') + ')', 'g'), '**')
console.log(r3)
var str="2[2[2[ab]2[cd]]3[c]]" // 拆开成对应次数
function pares(str){
if(!/(\d+)\[(a-zA-Z)+\]/g.test(str)) return str
str = str.replace(/(\d+)\[(a-zA-Z)+\]/g , function(item,$1,$2){
return $2.repeat($1)
})
return pares(str)
}
-
search()
-
语法: 字符串.search(正则表达式)
-
返回值: 有满足条件的片段,就是索引位置
-
没有满足条件的片段,就是-1
-
match()
-
语法: 字符串.match(正则表达式)
-
返回值: 没有全局g,和exec方法一样
-
有全局标识g,返回一个数组,捕获全部满足条件的片段组成的数组
// match 使用组匹配
console.log("1234567890".match(/^(\d{3})(\d{4})(\d{3})$/).slice(1))
-
split()
-
语法: 字符串.split(正则表达式)
-
返回值: 分割剩下的子串组成的数组
这里正则可以不用写g,因为split方法自己会匹配全局,这个和字符串本身方法的区别是可以写或
-
转载自:https://juejin.cn/post/7179423825472258105