什么是正则表达式?
使用特定的符号所描述的一种规则,用于字符串模式匹配,从而实现查找、校验和替换等功能。
如何创建正则表达式?
创建正则可以通过以下两种方式:
- 通过 RegExp 对象
new RegExp("RegExp", "i");
- 字面量形式, 用
//包裹构建的正则(常用)
/RegExp/i
RegExp 对象不常用,不做过多介绍,更详细的内容请挪步至:MDN-RegExp()
正则表达式特殊字符
| 符号 | 含义 |
|---|---|
| g | 修饰符,表示全局匹配 |
| i | 忽略匹配的大小写 |
| ^ | 匹配开头位置 |
| $ | 匹配结尾位置 |
| m | 多行匹配 |
| . | 通配符,除换行符等的任意字符 |
| [] | 字符组,表示可选项,只能为[]内的要求字符 |
| - | 连字符,省略字符组中的数据 |
| \s | 空白,即空格、换行、tab 缩进等(\S 为 \s 的非) |
| \w | 数字字母下划线 word(\W 为 \w 的非) 即 [a-zA-Z_] |
| \d | 一位数字 digit(\D 为 \d 的非) 即 [0-9] |
| \ | 转义字符 |
| ? | 前面字符连续出现零次或一次 0 | 1 |
| + | 前面字符连续出现一次或多次 >=1 |
| * | 前面字符连续出现零次或多次 >=0 |
| {m,n} | m、n 为数字,表示前面字符连续出现 m 到 n 区间中的次数 |
| \b | 边界符,它的前一个字符和后一个字符不全是 \w,\B 是其非,即除了 \b 的其他位置 |
| ?=p | 指 p 字符前面的位置,此位置后面匹配 p, ?!p 是 ?=p 的非 |
| ?<=p | 指 p 字符后面的位置,?<! 是 ?<=p 的非 |
| () | 表示捕获分组,会把每个分组里的匹配的值保存起来,使用 $n 获取,多层嵌套以最外层开始 |
| \n | n 为数字,反向引用,引用之前的匹配的第 n 个分组 |
| (?:) | 表示非捕获分组匹配的值不会保存 |
字符在正则中的使用
- g
'abcabc'.replace(/a/,'g')
// 'gbcabc' 当匹配到第一个 a 时会被替换,然后会停止匹配。此处涉及到贪婪模式和惰性模式,后续会介绍到
'abcabc'.replace(/a/g,'g')
// 'gbcgbc' 所有 a 都会被替换
- i
'abcABC'.replace(/a/g,'i')
// 'ibcABC' 只替换了 a
'abcABC'.replace(/a/gi,'i')
// 'ibciBC' a 和 A 都被替换
- ^ $
匹配开头结尾的 位置
'abc123'.replace(/^/g,'M')
// 'Mabc123' 查找到字符串开头的 位置
'abc123'.replace(/$/g,'M')
//'abc123M' 查找到字符串结尾的 位置
当用 ^ 和 $ 包裹的正则会实现精准匹配
/abc/.test('ABCabc')
// ture 该正则会去 'ABCabc' 中去查找,只要存在 abc 满足条件即可,所以返回 true
/^abc$/.test('ABCabc')
// false 该正则指开始和结尾只包裹 abc 的字符串,即匹配的字符串必须是 abc。从而实现精准匹配
- m
当 ^ 和 $ 单独与 g 组合时,只会匹配一行数据,此时需通过 m 和 g 配合实现多行匹配
'a\nb\nc\nb'.replace(/^\w+/g,'m')
// 'm\nb\nc\nb' 只有 a 被替换,其他换行数据并未匹配到
'a\nb\nc\nb'.replace(/\w+$/g,'m')
// 'a\nb\nc\nm' 只有最后一行 b 被替换
'a\nb\nc\nb'.replace(/^\w+/gm,'m')
// 'm\nm\nm\nm' 所有 m 都会被替换 /\w+$/gm 同理。
- .
. 除了换行符、回车符、行分隔符和段分隔符除外,即 \n\r\u2028\u2029 以外的任意字符
'abc123_!@#\n\r'.replace(/./g,'.')
// 'MMMMMMMMMM\n\r'
- []
[] 中可以放任意字符,含义为从 [] 中匹配一个字符
'abc123_!@#\n\r'.replace(/[abc123]/g,'M')
// 'MMMMMM_!@#\n\r'
- -
配合 [] 使用,达到省略的目的
'059_abcz'.replace(/[0-9a-z]/g,'-')
// '---_----' 0-9 十个数字以及 a-z 二十六个字母都会被匹配到
- \s \w \d
'\n a b 0 9 _'.replace(/\s/g,'s')
// 'ssasbs0s9s_'
'\n a b 0 9 _'.replace(/\w/g,'s')
// '\n s s s s s'
'\n a b 0 9 _'.replace(/\d/g,'s')
// '\n a b s s _'
- \
如何替换字符串的 . ?
'1.2'.replace(/./g,'-')
// '---',在正则中 . 表示几乎任意字符,要想匹配实际含义的 . 需要进行转译
'1.2'.replace(/\./g,'-')
// '1-2'
- ?
匹配个数 [0,1]
/^abc?$/.test('ab')
// true
/^abc?$/.test('abc')
// true,?前面的字符可要可不要
- *
匹配个数 [0,+∞)
/^abc*$/.test('ab')
// true
/^abc*$/.test('abcccc')
// true
- +
匹配个数 [1,+∞)
/^abc+$/.test('ab')
// false c 至少存在一个
/^abc+$/.test('abcccc')
// true
- {m,n}
1、{m,},匹配个数 [m,+∞),至少 m 个
2、{m,n},匹配个数 [m,n] 区间任意个数
3、{m} 和 {m,m} 含义一致,匹配个数 m
/^abc{2,3}$/.test('abcc')
// true
/^abc{2,3}$/.test('abccc')
// true
- \b
可以理解为匹配不连续 \w 的左右两边的 位置
'123 ace _ $%s^'.replace(/\b/g,'b')
// 'b123b baceb b_b $%bsb^',可以看到,只要字符 \w 左或右边不是 \w ,那么该位置就是 \b 位置
- ?=p
匹配 p 之前的 位置,换句话说该位置后面必须满足是 p(p 指正则、字符串等)
'123abc'.replace(/(?=a)/g,'?')
// '123?abc',找到了 a 前面的位置 ?=a 是整体需用 () 包裹
当 ?=p 在 ^ 前时,表示要匹配的字符串必须满足 p
/(?=[a-z])^[a-z0-9]*$/.test('123')
// false,(?=[a-z]) 表示字符串开头必须是一个字母,这样才能满足 (?=[a-z]) 在 ^ 之前;^[a-z0-9]*$ 表示字符串为数字字母。
/(?=[a-z])^123$/.test('a123')
// true
?!p 是 ?=p 的非,即除了 p 前面位置的其他位置
'123abc'.replace(/(?!a)/g,'?')
// '?1?2?3a?b?c?'
- ?<=p
和 ?=p 相反,?<=p 匹配的是 p 后面的 位置
'123abc'.replace(/(?<=a)/g,'?')
// '123a?bc',找到匹配字符 a 后面的位置
同理当 ?<=p 在 $ 后时,表示要匹配的字符串必须满足 p
/^[a-z0-9]*$(?<=[a-z])/.test('123')
// false,(?<=[a-z]) 表示字符串结尾必须是一个字母,这样才能满足 (?<=[a-z]) 在 $ 之后;^[a-z0-9]*$ 表示字符串为数字字母。
/^[a-z0-9]*$(?<=[a-z])/.test('123a')
// true
- ()
被 () 包裹叫捕获分组,可以通过 $n 进行获取
'2022-02-24'.replace(/(\d{4})-(\d{2})-(\d{2})/,'$3/$2/$1')
// '24/02/2022' ,每个 () 都会形成捕获组,可以通过 $n 来获取它们
当多层 () 时,按照从左到右的顺序进行组的划分
'abc-123- '.replace(/((\w{3}-(\d{3}))-(\s*))/,(_,$1,$2,$3,$4)=>{console.log($1,"$",$2,"$",$3,"$",$4,"$")})
// abc-123- $ abc-123 $ 123 $ $
上例可以看出
$1: 为最外层 ((\w{3}-(\d{3}))-(\s*) 匹配整个字符串 'abc-123- '
$2: 为 (\w{3}-(\d{3})) 匹配字符串 'abc-123'
$3: 为 (\w{3}-(\d{3})) 包裹的 (\d{3}) 匹配为 '123'
$4: 为 (\s*) 匹配剩余空格 ' '
由此可以看出分组的规则:从左到右递归匹配
- \n
写一个支持 2016-06-12、2016/06/12、2016.06.12 三种格式的正则
/\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/.test('2016-06-12');
// true
/\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/.test('2016-06/12');
// true,可以看出上述正则存在问题可以存在前后连接的字符不一致的情况
/\d{4}(-|\/|\.)\d{2}\1\d{2}/.test('2016-06/12');
// false
/\d{4}(-|\/|\.)\d{2}\1\d{2}/.test('2016-06-12');
// true
由案例可以看出 \n 反向引用(只能引用之前出现的分组),匹配的是与之对应分组匹配的结果。
- (?:)
'2022-02-04'.replace(/(?:\d{4})-(\d{2})-(\d{2})/,(_,$1,$2)=>{console.log($1,$2)})
// 02 04,由此可以看出 (?:) 的效果
正则可视化工具
regexper,图形化的方式解析正则,让你更清晰的洞悉正则要表达的含义。
正则方法
| 方法 | 含义 |
|---|---|
| match | 找到符合正则的所有数据,以数组的形式返回 |
| replace | 找到符合正则的数据并替换 |
| search | 查询正则所寻找的字符位置 |
| test | 对字符串进行校验,以 boolean 值返回结果 |
- match
'ab'.match(/a/) => ['a', index: 0, input: 'ab', groups: undefined]
含义:
groups: 一个捕获组数组 或 undefined(如果没有定义命名捕获组)
index: 匹配的结果的开始位置
input: 搜索的字符串
- replace
- 第一个参数非正则:匹配到第一个字符串内容,用第二个进行替换(非贪婪匹配)
'aba'.replace('a',1) => '1ba'
- 第一个参数为正则,第二个参数为非 $ 字符: 将符合正则的数据进行替换
'aba'.replace(/a/g,1) => '1b1'
- 第一个参数为正则,第二个参数为 $ 字符: 将符合正则的数据用 $ 规则进行替换
'1a2b'.replace(/([0-9])([a-z])/g,'$1.') => '1.2.'
'1a2b'.replace(/([0-9])([a-z])/g,'$&.') => '1a.2b.'
'start 1a end'.replace(/([0-9])([a-z])/,"-$`")
=> 'start -start end'
'start 1a end'.replace(/([0-9])([a-z])/,"-$'")
=> 'start - end end'
'1a2b'.replace(/([0-9])([a-z])/g,"$$.") => '$.$.'
$ 含义:
$i(i 取值范围 1~99):表示从左到右正则子表达式所匹配的文本
$&:表示与正则表达式匹配的全部文本
$`:表示匹配字符串的左边的所有文本
$':表示匹配字符串的右边的所有文本
$$:表示 $ 转译
- 第一个参数为正则,第二个为函数:
var a = '1a2b'.replace(/([0-9])([a-z])/g,(match,$1,$2,index,s)=>{
console.log(match,$1,$2,index,str);
return $1
})
// 1a 1 a 0 1a2b
// 2b 2 b 2 1a2b
// a = 12
- search
'hello World'.search(/[A-Z]/g)
// 6
- test
/hello/.test('hello World')
// true
贪婪匹配和非贪婪匹配(惰性匹配)
- 贪婪匹配,匹配时如果遇到
+,?,*,{n},{n,},{n,m}标识符,代表是贪婪匹配,会尽可能多的去匹配内容
var str='aacbacbc';
var reg=/a.*b/;
var res=str.match(reg);
- 非贪婪模式,正则表达式去匹配时,会尽量少的匹配符合条件的内容,一旦发现匹配符合要求,立马就匹配成功,而不会继续匹配下去。(除非有
g,会开启下一组匹配,全局匹配)。或者在贪婪模式的标识符后面加上一个?。
var str='aacbacbc';
var reg=/a.*?b/;
var res=str.match(reg);
原理:贪婪是先吃进,回溯再让出,非贪婪是先忽略,回溯再吃进。
练习
- 在
<div id="container" class="main"></div>中提取出 id="container"。 - 数字的千位分隔符表示法,例:把"12345678",变成"12,345,678"。
- 密码长度 6-12 位,由数字、小写字符和大写字母组成,但必须至少包括 2 种字符。
答案
/id="[^"]*"//(?!^)(?=(\d{3})+$)/g;/((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^[0-9A-Za-z]{6,12}$/或者/(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/
Q & A
如果要匹配任意字符?
使用 [\d\D]、[\w\W]、[\s\S] 和 [^] 中任何的一个。
输入只能是汉字?
使用 [\u4e00-\u9fa5] 匹配