RegExp 正则表达式
常用的元字符
量词元字符
-
*****零到多个
-
**+**一个到多个
-
**?**零次或一次
-
{n} 匹配n次
-
{n,} 出现n到多次
-
{n,m} 出现n到m次
特殊元字符 -
\ 转义元字符:单个或者组合在一起代表特殊含义
-
. 除了\n(换行符)以外的任意字符
-
^ 以哪一个元字符作为开始
-
$ 以哪一个元字符作为结束
-
\n 换行符
-
\d 0~9之间的数字
-
\D 匹配一个非数字字符.
-
\w 数字,字母,下划线 中的任意一个字符
-
\s 一个空白字符
-
\t 一个制表符
-
x|y x或y中的一个字符 一般不单独使用 配合()使用
-
[xyz] x或者y或者z中的一个字符
-
[^xyz] 除了x,y , z中的任意一个字符
-
[a-z] 指定 a-z这个范围中的任意字符 [0-9a-zA-Z]===\w
-
[^a-z] 除了a到z
-
() 正则中的分组符号
- 优先计算
- 分组捕获
- 分组引用
-
(?:) 只匹配不捕获
-
(?=) 正向预查 后面是个条件但是不匹配也不捕获 exp1(?=exp2) exp1后边是exp2就匹配
-
(?!) 反向预查 后面是个条件但是不匹配也不捕获 exp1(?!exp2) exp1后边不是exp2就匹配
正则表达式常用修饰符 -
i 忽略大小写匹配
-
m 可以进行多行匹配
-
g 可以进行全局匹配
实现正则捕获的办法
- 正则RegExp.prototype上的方法
- exec
- test
- 字符串String.prototype上支持正则表达式的处理方法
- replace
- match
- split
看下面例题
let reg = /\d$/,
str1 = "yzl2021",
str2 = "2021yzl",
str3 = "yz2021l";
console.log(reg.test(str1)); //->true
console.log(reg.test(str2)); //->false
console.log(reg.test(str3)); //->false
//分析:$代表结尾的意思 说明以数字结尾就算匹配上
let reg = /^2.3$/;
console.log(reg.test(2.3)); //->true
console.log(reg.test('2@3')); //->true
console.log(reg.test(23)); //->false
//分析: .可以代表除了\n的元字符
let reg = /^2\.3$/;
console.log(reg.test(2.3)); //->true
console.log(reg.test('2@3')); //->false
//分析: \把.给转义了所以2@3匹配不上
let str = "\\d",
reg = /^\d$/;
console.log(reg.test(str)); //->false
reg = /^\\d$/;
console.log(reg.test(str)); //->true
正则表达式验证是否为有效数字
let reg = /^[+-]?\d+(\.\d)?$/;
console.log(reg.test('10')); //=>true
console.log(reg.test('10.1')); //=>true
console.log(reg.test('-10.1')); //=>true
验证有效密码
//密码为字母数字下划线 6-10位数字
let reg = /^\w{6,10}$/;
console.log(reg.test("qwerty")) //=>true
console.log(reg.test("qwert")) //=>false
验证真实姓名
/*
1.中文汉字的编码[\u4E00-\u9FA5]
2.名字长度2-10
*/
let reg = /^[\u4e00-\u9fa6]{2,10}(\·[\u4e00-\u9fa6]{2,10})?$/;
console.log(reg.test('切格瓦拉'));
分组捕获
/*
1.使用场景 比如:2021 我要使第一位和第三位相同
2.()表示捕获 \1表示与第一个捕获相同 \2表示与第 二个捕获相同
*/
let reg = /^(\d)\d\1\d$/;
console.log(reg.test('2021')); //=>true
console.log(reg.test('2031')) //=>false
正则捕获的懒惰性
/*
1.exec方法只能获取一个捕获
*/
let reg = /[\u4e00-\u9fa0]/g;
console.log(reg.exec("我2在0学2习1"));
/*
[0: "我"
groups: undefined
index: 0
input: "我2在0学2习1"
length: 1]
*/
/*
2然后我们又想获取所有匹配上的数据我们就可以自己封装一个 Allexec方法
*/
let reg = /[\u4e00-\u9fa0]/g;
// console.log(reg.exec("我2在0学2习1"));
RegExp.prototype.Allexec = function (str){
// 大捕获
let bigarr = [],
// 小捕获
smallarr = [];
if(!this.global){
bigarr.push(this.exec(str)[0]);
smallarr.push(this.exec(str)[0]);
return {
bigarr,
smallarr
}
}else{
let data = this.exec(str);
while(data){
bigarr.push(data[0]);
smallarr.push(data[0]);
data = this.exec(str);
}
return {
bigarr,
smallarr
}
}
}
console.log(reg.Allexec("我2在0学2习1"));
/*
bigarr: (4) ["我", "在", "学", "习"]
smallarr: (4) ["我", "在", "学", "习"]
*/
/*
3.我们也可以使用字符串中的match方法来批量捕获
*/
let str = "我2在0学2习1",
reg = /[\u4e00-\u9fa0]/g;
console.log(str.match(reg));
//=> ["我", "在", "学", "习"]
正则表达式的贪婪性
/*
1.正则表达式具有贪婪性,默认捕获最长的.
2.在量词后面加上?就可以取消贪婪性
3.取消贪婪性就匹配最短的
比如:+是匹配1到多个+?就匹配1个字符为一组
*是匹配0到多个*?就匹配0个字符为一组
{2,4} 就匹配2个字符为一组
*/
let reg = /\d+/g;
console.log('2021'.match(reg));
//=> ["2021"]
let reg = /\d+?/g;
console.log('2021'.match(reg));
//=> ["2", "0", "2", "1"]
let reg = /\d*?/g;
console.log('2021'.match(reg));
//=> ["", "", "", ""]
let reg = /\d{2,}?/g;
console.log('2021'.match(reg));
//=> ["20", "21"]
其他正则的捕获方法
test也能捕获(本意是匹配)
let reg = /(\d+)/g;
console.log(reg.test("2我0要努2力1")); //=>true
console.log(RegExp.$1); //=>2
console.log(reg.test("2我0要努2力1")); //=>true
console.log(RegExp.$1); //=>0
replace 方法捕获
let reg = /sjl/;
let str = "sjl是个机灵鬼";
str = str.replace("sjl","yzl");
console.log(str);
/*
下面看一个不用正则的弊端
*/
let reg = /sjl/g;
let str = "sjl是个机灵鬼sjl";
str = str.replace("sjl","sjl儿子").replace("sjl","sjl儿子");
console.log(str); //=>sjl儿子儿子是个机灵鬼sjl
/*
replace 正则表达式的匹配规则
1.首先拿REG和Str进行匹配捕获,能匹配几次就会把传递的函数执行几次(而且是匹配一次就执行一次)
2.不仅执行方法,而且replace 还给方法传递了实参(和exec捕获的内容一致的信息:大正则匹配的内容,小分组匹配的信息...)
3.replace 回调函数返回的return是直接替换大捕获
*/
//第一种写法
let time = "2021/4/26",
reg = /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/;
time = time.replace(reg,function(content,$1,$2,$3){
return $1+'-'+$2+'-'+$3
})
console.log(time) //2021-4-26
//第二种写法
let time = "2021/4/26",
reg = /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/;
time = time.replace(reg,function(...arg){
//数组的结构赋值 如果不要大捕获content就 let [_,$1,$2,$3] = arg;或者let [,$1,$2,$3] = arg 占位;
let [content,$1,$2,$3] = arg;
return $1+'-'+$2+'-'+$3
})
console.log(time); //2021-4-26
单词首字母大写
let str = "hellow world",
reg = /\b([a-z])[a-z]*\b/g;
str = str.replace(reg,function(...arg){
let [content,$1,$2,$3] = arg;
return $1.toUpperCase()+content.substring(1);
})
console.log(str) //=>Hellow World
统计一个字符串中出现最多的字母
/*
方法一:对象去重
*/
let str = "kknuwesadqwkkgxf",
obj = {},
max = 0,
//出现最多次的字符串数组
code = [];
//调用数组的forEach方法。并且用call更改this指向str
[].forEach.call(str,(v,i)=>{
//如果不是undefined 就说明字符已经出现过了所以就++
//也可以这样写 obj.hasOwnProperty(v) obj对象中是否出现过v属性
if(obj[v]!=undefined){
obj[v]++;
}else{
obj[v] = 1;
}
})
//找到最大值
for (const key in obj) {
if(obj[key]>max){
max = obj[key];
}
}
//压入数组
for (const key in obj) {
if(obj[key]===max){
code.push(key);
}
}
console.log(code); //=>["k"]
/*
方法二:正则表达式匹配的方法
*/
let str = "kknuwesadqwppppkkgxf",
reg =/([a-zA-Z])\1+/g,
newdata = [],
maxlen = 0,
code = [];
/*
1.str.split("") 字符串变成数组
2.sort((a,b)=>a.localeCompare(b)) 数组排序(字符串比较用localeCompare)
3.join("") 重新拼接成字符串
*/
str = str.split("").sort((a,b)=>a.localeCompare(b)).join(""); //=>"adefgkkkknppppqsuwwx"
newdata = str.match(reg).sort((a,b)=>b.length-a.length); //=>["kkkk", "pppp", "ww"]
maxlen = newdata[0].length;
for (const v of newdata) {
if(v.length === maxlen) code.push(v.substr(0,1))
}
console.log(code); //=>["k", "p"]
/*
正则方法二
思路:求字符串长度(str)然后用正则去匹配 let reg = new RegExp("([a-zA-Z])\\1{"+(i-1)+"}",'g');
如果能匹配上就说明已经匹配到了最长的字符串 因为i是str.length--下来的
*/
let str = "kknuwesadqwppppkkgxf",
strlen = str.length,
flat = false,
codearr = [];
for(let i =strlen;i>0;i--){
let reg = new RegExp("([a-zA-Z])\\1{"+(i-1)+"}",'g');
str.split('').sort((a,b)=>a.localeCompare(b)).join("").replace(reg,function(...arg){
let [_,$1] = arg;
codearr.push($1);
flat = true;
})
if(flat) break;
}
console.log(codearr)
/*
正则方法三
思路:1.每次匹配一个字符
2.然后用正则匹配把str中相同的字符全删除
3.比较删除之前和删除之后str的字符在differ存储差值
4.if(differ>=max) 如果差值大于max就存对象中并存储max 因为存obj中的值是假设最大
5.forin 遍历obj 如果 max === obj[key]就是出现最多的字符
*/
let str = "kknuwesadqwppppkkgxf",
letter = '',
codearr = [],
max = 0,
oldlen,
newlen,
obj = {},
differ=-1;
//只要str没有变成空
while(str!==''){
//存储之前未删除的str长度
oldlen = str.length;
letter = str[0];
let reg = new RegExp(letter,'g');
str = str.replace(reg,"");
newlen = str.length;
differ = oldlen - newlen;
if(differ>=max){
obj[letter] = differ;
max = differ;
}
}
for (const key in obj) {
if(max === obj[key]){
codearr.push(key);
}
}
console.log(codearr)
使用正则表达式 自定义格式化时间格式
/*
* 思路:1.首先第一步,有一个系统的时间 let time = '2021/4/27 17:01';
2.使用let timearr = time.match(reg) 把系统时间都给捕获出来
3.使用template.replace(reg,function(){})使用timearr数组数据替换模板
*/
//系统时间
let time = '2021/4/27 17:01';
//在单例模式中修改string的原型
~function(){
//如果没有传格式就默认"{0}年{1}月{2}日 {3}时{4}分{5}秒"
function formatTime(template = "{0}年{1}月{2}日 {3}时{4}分{5}秒"){
//把系统时间用正则分离出来
let timeArr = this.match(/\d+/g);
//替换template中的{index}
return template.replace(/\{(\d+)\}/g,(...[,$1]) => {
//如果$1大于数组长度就"00"
let timedata = timeArr[$1] || "00"
//如果只有单个数字就拼接'0'
timedata.length<2?timedata='0'+timedata:null;
return timedata;
})
}
//这个就很高级要学会
["formatTime"].forEach((v,i) => {
// eval(v) ==>返回一个函数 v返回一个字符串
String.prototype[v] = eval(v);
})
}()
console.log(time.formatTime("{0}年{1}月{2}日"))
正则实现queryURLParams 把GET参数提取到obj对象中
//正则实现queryURLParams
~function(){
function queryURLParams(){
let obj = {};
// 获得参数params
this.replace(/([^%=?#]+)=([^%=?#]+)/,(...[_,$1,$2]) => {
obj[$1] = $2;
return ;
})
//获得锚点
let reg = /#([^%=?#]+)/;
obj["HASH"] = reg.exec(this)[1]
return obj;
}
["queryURLParams"].forEach((v,i) => {
// eval(v) ==>返回一个函数 v返回一个字符串
String.prototype[v] = eval(v);
})
}()
let str = "https://www.mosoteach.cn/web/index.php?c=clazzcourse#kkk";
console.log(str.queryURLParams());
正则表达式实现千分符分割数据
//正则表达式实现千分符
let str = '12321321321313';
~function(){
function micrometer(){
/*
1.开头可以是(\d{1,3})1-3个数字
2.(\d{1,3})(?=(\d{3})+$)
+(\d{1,3})后面必须是3个数字为一组
+$标识结尾也必是是3个数字
*/
let reg = /(\d{1,3})(?=(\d{3})+$)/g;
return this.replace(reg,(...[content]) => {
console.log(content);
return content+',';
});
}
['micrometer'].forEach((v,i) => {
String.prototype[v] = eval(v);
})
}()
console.log(str.micrometer());
传统方法实现数字千分符
//字符串分割的方法实现千分符
/*
思路:1.字符串转变为数组
2.数组取反
3.for循环拼接,
4.数组取反变为字符串输出
*/
let str = '123213321313';
~function(){
function micrometer(){
let str = this.split('').reverse().join("");
for (let index = 2; index < str.length-1; index=index+4) {
let prestr = str.substring(0,index+1)+',',
nextstr = str.substring(index+1,str.length);
str = prestr + nextstr;
}
return str.split("").reverse().join("");
}
['micrometer'].forEach((v,i) => {
String.prototype[v] = eval(v);
})
}()
console.log(str.micrometer());