什么是正则表达式?
正则表达式(Regular Expression,简称 Regex)是一种用于描述文本匹配模式的强大工具。它由一系列字符和特殊符号组成,可以简洁地描述复杂的文本模式。正则表达式常用于字符串搜索、替换、分词等文本处理任务。
正则表达式是每个开发人员必须掌握的一项技能。使用正则表达式可以在处理一些任务时非常方便快捷。
在线测试工具
- 将正则表达式以流程图式展示:regexper.com/;
- 详细展示匹配的次数:regexr.com/, 可以通过短链接创建分享,【但是不能写后行断言】
- 比如匹配html标签里的内容,就可以访问该 regexr.com/7ut2h
- 比如3个数字前的内容,并且是从开头算起,regexr.com/7ut2n
- regex101.com/,【可以进行后行断言,网络不稳定】
记忆口诀
使用顺口溜记住些常用的元字符,及匹配规则,就可以满足日常使用。
-
w s d b d:常用的几个元字符,简称我说的都对;
- 我 [w 字母数字下划线] 说 [ s 空白字符] 吧 [b, 边界] 都 [逗号 .] 对[d 0-9的数字]
-
小卒[分组] 中活[或者] 大量[量词]
- 小括弧():是分组,在象棋中,卒是小兵,
- 中括弧[]:是选择其中的一个;在中国生活
- 大括弧{}:是量词;
-
关于小括弧分组的使用:
- 引用分组使用:反斜扛 + 编号:
\number
;/\d{3}-(\d{5})-\1/
;其中\1表示使用第一个分组的内容; 内容必须一致才能匹配- 案例
/\d{3}-(\d{5})-\1/
可以匹配 021-34422-34422
- 问冒 不存分组(?:), 不保存分组;
/\d{3}-(?:\d{5})-\1/
就无法匹配 021-34422-34422- 案例:
\d{3}-(?:\d{5})+(([a-z])+)-\1+
可以匹配: 021-34422+ha-ha
- 案例:
- 引用分组使用:反斜扛 + 编号:
-
零宽断言:等号是肯定,叹号是否定,<符号是左移
语法
元字符、量词、修饰符、分组、贪婪与非贪婪
元字符
量词
元字符 | 含义 |
---|---|
* | 0个或多个 |
? | 0个或一个 |
+ | 一个或多个 |
{n} | 出现n次 |
{n, m} | 出现n到m次 |
{n,} | 出现n次或多次 |
修饰符
- i :ignoreCase 表示不区分大小写
- g : global 表示全局匹配
- m : multiline 表示多行匹配
- s 表示允许.匹配换行符
- u 使用unicode码的模式进行匹配
- y 执行"粘性(sticky)"搜索,匹配从目标字符串的当前位置开始。
贪婪或惰性
在正则表达式中,使用量词修饰符进行匹配采用的是贪婪模式,就是尽可能多的匹配。
/.*/
是单个非空白字符匹配任意次,即贪婪匹配
js
// 默认的贪婪模式匹配,会匹配到最后边的}符合,即把中间的years old,Bob is内容给替换掉了
var str = 'Anna is {age} years old,Bob is {age} years old too';
var expr = /{.*}/g;
console.log(str.replace(expr, '13'));
/*输出: Anna is 13 years old too*/
/.*?/
是满足条件的情况只匹配一次,即懒惰匹配
js
// 懒惰匹配,匹配到}就立马结束。把两个{age}都匹配是因为g修饰符
var str = 'Anna is {age} years old,Bob is {age} years old too';
var expr = /{.*?}/g;
console.log(str.replace(expr, '13'));
/*输出: Anna is 13 years old,Bob is 13 years old too*/
分组
使用小括弧()是可以将正则进行分组。分组之后还可以对分组的内容进行再次复用;
默认分组是保存分组
js
let reg = /\d{3}-(\d{5})-\1/; // \1表示对第一个分组进行引用,匹配的值必须和第一个组内的值保持一致
let str = "021-34422-34422"; // 34422就是第一个分组的内容
reg.test(str); // true
可以通过 ?:
不保存分组
js
let reg = /\d{3}-(?:\d{5})\+(([a-z])+)-\1+/g; //第一个分组是5个数字,不保存第一个分组,只会匹配([a-z])+分组的内容
let str = '021-34422+ha-ha';
reg.test(str);
零宽断言
只断言不捕获,如同 ^ 代表开头, $ 代表结尾,\b 代表单词边界一样,先行断言和后行断言也有类似的作用,它们只匹配某些位置,不占用字符,所以被称为 "零宽" 。
正则表达式的先行断言和后行断言一共有 4 种形式:
- (?=pattern) 零宽正向先行断言 (zero-width positive lookahead assertion)
- (?!pattern) 零宽负向先行断言 (zero-width negative lookahead assertion)
- (?<=pattern) 零宽正向后行断言 (zero-width positive lookbehind assertion)
- (?<!pattern) 零宽负向后行断言 (zero-width negative lookbehind assertion)
这里面的pattern 是一个正则表达式。 < 称之为左移符号
先行断言(lookahead)
x(?=p) 正向先行断言
该 x 位置右侧能够匹配 p;
例如对"a regular expression",要想匹配 regular 中的 re,但不能匹配 expression 中的 re,可以用re(?=gular),该表达式限定 re 右边的位置,这个位置之后是 gular,但并不捕获 gular 这些字符。
x(?!p) 否定先行断言
该x位置右侧的 不能匹配 p。
例如对"regex represents regular expression",要想匹配除 regex 和 regular 之外的 re,可以用re(?!g),该表达式限定了re右边的位置,这个位置后面不是字符g。
后行断言(lookbehind),添加 < 符号
之所以叫后行断言,是因为正则表达式引擎在匹配字符串和表达式时,是从前向后逐个扫描字符串中的字符,并判断是否与表达式符合,当在表达式中遇到该断言时,正则表达式引擎需要往字符串前端检测已扫描过的字符,相对于扫描方向是向后的。
(?<=p)x 正向后行断言
该x位置左侧的字符序列能够匹配 p。
例如对regex represents regular expression ,要想匹配单词内部的 re,但不匹配单词开头的 re,可以用 (?<=\w)re ,单词内部的 re,在 re 前面应该是一个单词字符。
(?<!p)x 否定后行断言
该x左侧位置的字符序列不能匹配 p。
例如对 "regex represents regular expression" 这个字符串,要想匹配单词开头的 re,可以用 (?<!\w)re 。单词开头的re ,在本例中,也就是指不在单词内部的re ,即re前面不是单词字符。当然也可以用**\bre**来匹配。
正则的方法exec和test
exec的返回值是数组,test的返回值是布尔值。
exec的使用
js
let words= "happy learn RegExp's exec method";
let regx=/(\w{3})Ex(.{3})/g; //第一个括号可以用\w进行匹配第二个必须用.才能匹配到',否则为null
console.log(regx.exec(words));
// 返回数组
[
"RegExp's",
'Reg',
"p's",
index: 12,
input: "happy learn RegExp's exec method",
groups: undefined
]
exec()接受一个字符串参数,返回包含第一个匹配项信息的数组;如果没有匹配项的情况下返回null 返回的数组是Array实例,并且包含额外的属性: index 和 input, groups。
其中,
- index 表示匹配项在字符串中的位置。(匹配项为RegExp's ,对应的位置是16);
- input 表示应用正则表达式的字符串。(happy learn RegExp's exec method);
在数组中,
- 第一项:表示与整个模式匹配的字符串 (代码中的 RegExp's 匹配 正则校验);
- 其它项: 与表达式中用()包裹起来的,即捕获组匹配的字符串,第二项表示第一个捕获组的值,第三项表示第二个括号捕获的值(如果模式中没有捕获组regx = /\w{3}Ex.{3}/g;,则该数组只包含一项即RegExp's)
test的使用
js
let words = "happy learn RegExp's test method";
let regx = /\w{3}Ex.{3}/g;
let regOk = /\w{3}Ok.{3}/g;
console.log(regx.test(words)); // true
console.log(regOk.test(words)); // false
字符串的方法match和replace
match
match的使用和exec的作用一样,只不过是string身上的方法。
js
let words = "happy learn string's match method";
console.log(words.match(/(\w{3})ing(.{2})/));
// 返回结果
[
"string's",
'str',
"'s",
index: 12,
input: "happy learn string's match method",
groups: undefined
]
replace
提交掉字符串中符合正则的值
js
// 匹配值并进行替换
var re = /apples/gi;
var str = "Apples are round, and apples are juicy.";
var newstr = str.replace(re, "oranges");
console.log(newstr);
//输出内容 oranges are round, and oranges are juicy.
js
// 打印出通过()捕获的值
let words = "happy learn string's replace method";
words.replace(/(\w{3})ing(.{2})/g, function ($1, $2) {
console.log($1, $2); // $1 $2分别为两个()捕获到的内容
});
js
// 将所有单词首字母大写
let words = "hello everybody, come on!";
console.log(
words.replace(/\b\w*\b/g, (word) => {
return word.substring(0, 1).toUpperCase() + word.substring(1);
})
);
// Hello Everybody, Come On!
js
// 将句首字母大写
let words = "everybody, go! ";
console.log(words.replace(/^\b(\w)/g, (L) => L.toUpperCase()));
常用的正则示例
校验数字的表达式
js
let reg = /^[0-9]*$/; // 数字
reg = /^\d{n}$/; // n位数字
reg = /^\d{n,}$/; // 至少n位的数字
reg = /^\d{m,n}$/; // m至n位的数字
reg = /^(0|[1-9][0-9]*)$/; // 零和非零开头的数字
reg = /^([1-9][0-9]*)+(\.[0-9]{1,2})?$/; // 非零开头的最多带两位小数的数字
reg = /^(\-)?\d+(\.\d{1,2})$/; // 带1-2位小数的正数或负数
reg = /^(\-|\+)?\d+(\.\d+)?$/; // 正数、负数、或小数
reg = /^[0-9]+(\.[0-9]{2})?$/; // 有两位小数的正实数
reg = /^[0-9]+(\.[0-9]{1,3})?$/; // 有1~3位小数的正实数
reg = /^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$/ 或 reg= /^\+?[1-9][0-9]*$/; // 非零的正整数
reg = /^\-[1-9][]0-9"*$/ 或 reg = /^-[1-9]\d*$/; // 非零的负整数
reg = /^\d+$/ 或 reg = /^[1-9]\d*|0$/; // 非负整数
reg = /^-[1-9]\d*|0$/ 或 reg = /^((-\d+)|(0+))$/; // 非正整数
reg = /^\d+(\.\d+)?$/ 或 reg = /^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$/; // 非负浮点数
reg = /^((-\d+(\.\d+)?)|(0+(\.0+)?))$/ 或 reg = /^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$/; // 非正浮点数
reg = /^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$/ 或 reg = /^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$/; // 正浮点数
reg = /^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$/ 或 reg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/; // 负浮点数
reg = /^(-?\d+)(\.\d+)?$/ 或 reg = /^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$/; // 浮点数
验证是有效数字
规则分析:
- 开头可能是-或者+,也可能不出现; [-+]? 或者(-|+)?
- 第一位是0-9都可以,0不能重复出现在首部; (\d|([1-9]\d+))
- 小数部分可能存在,也可能不存在;但是如果有小数点,则点后边必须有数字;(.\d+)?
js
let reg = /^[-+]?(\d|([1-9]\d+))(\.\d+)?$/;
let num = '0.569';
reg.test(num); // true
num = '+1369';
reg.test(num); // true
num = '-2.569';
reg.test(num); // true
num = '-002.569';
reg.test(num); // false
校验字符的表达式
js
let reg = /^[\u4e00-\u9fa5]{0,}$/; // 汉字
reg = /^[A-Za-z0-9]+$/ 或 reg = /^[A-Za-z0-9]{4,40}$/; // 英文和数字
reg = /^.{3,20}$/; // 长度为3-20的所有字符
reg = /^[A-Za-z]+$/; // 由26个英文字母组成的字符串
reg = /^[A-Z]+$/; // 由26个大写英文字母组成的字符串
reg = /^[a-z]+$/; // 由26个小写英文字母组成的字符串
reg = /^[A-Za-z0-9]+$/; // 由数字和26个英文字母组成的字符串
reg = /^\w+$/ 或 reg = /^\w{3,20}$/; // 由数字、26个英文字母或者下划线组成的字符串
reg = /^[\u4E00-\u9FA5A-Za-z0-9_]+$/; // 中文、英文、数字包括下划线
reg = /^[\u4E00-\u9FA5A-Za-z0-9]+$/ 或 reg = /^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$/; // 中文、英文、数字但不包括下划线等符号
reg = /[^%&',;=?$\x22]+/; // 可以输入含有^%&',;=?$\"等字符
reg = /[^~\x22]+/; // 禁止输入含有~的字符
特殊需求表达式
js
let reg = /[1-9][0-9]{4,}/; // 腾讯QQ号(腾讯QQ号从10000开始)
reg = /[1-9]\d{5}(?!\d)/; // 中国邮政编码
reg = /((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/; // IPv4地址
reg = /^\d{4}-\d{1,2}-\d{1,2}/; // 日期格式
reg = /^(0?[1-9]|1[0-2])$/; // 一年的12个月(01~09和1~12)
reg = /^((0?[1-9])|((1|2)[0-9])|30|31)$/; // 一个月的31天(01~09和1~31)
reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/; // 身份证号(15位、18位数字),最后一位是校验位,可能为数字或字符X
reg = /\d{3}-\d{8}|\d{4}-\d{7}/; // 国内电话号码(0511-4405222、021-87888822)
reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/; // Email地址
reg = /[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/; // 域名
reg = /[a-zA-z]+://[^\s]*/ 或 reg = /^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$/; // InternetURL
reg = /^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/; // 手机号码
reg = /((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)/; // 电话号码正则表达式(支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号)
reg = /^[a-zA-Z][a-zA-Z0-9_]{4,15}$/; // 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)
reg = /^[a-zA-Z]\w{5,17}$/; // 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线)
// ============强密码==============
reg = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{8,10}$/; // 必须包含大小写字母和数字的组合,不能使用特殊字符,长度在 8-10 之间
reg = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$/; // 必须包含大小写字母和数字的组合,可以使用特殊字符,长度在8-10之间
reg = /((?=.*[a-z])(?=.*\d)|(?=[a-z])(?=.*[<>;':",.$#@!~%^&*])|(?=.*\d)(?=.*[<>;':",.$#@!~%^&*]))[a-z\d<>;':",.$#@!~%^&*]{8,16}/; // 可输入特殊字符、字母、数字,必须包含其中两种,长度在8-16之间