javascript 正则表达式 进阶教程

学习之前先来说一说一些概念

 

子项

  1.正则的一个分组为一个子项,子项的匹配结果可以在这个子项之后被使用

  2.子项是有顺序的,以(出现的位置顺序从左到右,第一个'()'--分组 包含的为第一子项,第二个为第二子项,一次类推)

  3.可以在子项后面使用\n的形式来反向引用子项的匹配结果.(n为子项的序号,\n表示的是子项匹配到的结果,而不是子项规则本身)

 

 

replace方法:

//replace 参数用法

//1. 第一个参数为字符串
let testStr = "abc123";
console.log(testStr.replace("abc","456")); //456123
//2. 第一个参数为正则表达式
console.log(testStr.replace(/[a-z]+/,456));//456123
//3. 第二个参数为function,必须要有返回值,否则默认返回 'undefined'
console.log(testStr.replace(/[a-z]+/,function(){
    return ;
})) //undefined123
console.log(testStr.replace(/[a-z]+/,function(){
    return 456;
})) //456123

 

//第二个参数为字符串时,还可以插入以下的特殊变量来引用子项或与子项相关的内容
let testStr = "abc123def";
//1. $$ 表示 '$'
console.log(testStr.replace(/\d+/g,"$$")); //abc$def
//2. $& 表示 插入的匹配字串
console.log(testStr.replace(/\d+/g,"$&")); //abc123def   
console.log(testStr.replace(/\d+/g,"-$&")); //abc-123def    
//3. $`表示 插入的匹配字串之前的字符
console.log(testStr.replace(/\d+/g,"$`")); //abcabcdef   
//4. $'  表示 插入的匹配字串之后的字符
console.log(testStr.replace(/\d+/g,"$'")); //abcdefdef  
console.log(testStr.replace(/\d+/g,"-$'-")); //abc-def-def
//5. $n  表示 插入的匹配到子项的下标 
let re = /((\d)+)/; //最外层的((\d)+)子项为第一子项, (\d)为第二子项
console.log(testStr.replace(re,"$1")); //abc123def  第一个子项匹配到的结果为123
console.log(testStr.replace(re,"$2")); //abc3def 第二个子项匹配到的结果为3. (子项匹配到多个值的话会返回最后一个值,因此匹配到了123,返回3)

 

//replace 第二个参数为 function时,function里面的参数是什么?

let email = "[email protected]";

let re = /\[email protected]\w+.[a-z]{2,4}/; //没有子项的正则 
let newStr = email.replace(re,function(res,index,origin){    
        //如果正则表达式没有子项,则function会返回三个参数,第一个参数为匹配正则的结果,第二个是这个结果开始位置的下标,第三个是被处理的字符串
        console.log(res); //[email protected]
    console.log(index); //0
    console.log(origin); //[email protected]
    return "123";
})
console.log(newStr); //123

let re2 = /(\w+)@(\w+).([a-z]{2,4})/; //有子项的正则 
let newStr2 = email.replace(re2,function(res,child1,child2,child3,index,origin){    
    //function会返回多个参数,第一个参数为匹配正则的结果,到处第二个是子项最后匹配到的下标位置,最后一个是被处理的字符串,中间的参数则是依次匹配到子项值
    console.log(res); //[email protected]
    console.log(child1); //983521365
    console.log(child2); //qq
    console.log(child3); //com
    console.log(index); //0
    console.log(origin); //[email protected]
    return "123";
})
console.log(newStr2); //123            

 

 使用子项参数修改正则返回值

//将邮箱的用户名全部替换为大写字母
let email = "[email protected]";
let re2 = /(\w+)@(\w+).([a-z]{2,4})/; //有子项的正则 
let newStr = email.replace(re2,function(res,child1,child2,child3,index,origin){
    return child1.toUpperCase() + "@" + child2 + "."+child3;
})
console.log(newStr); //[email protected]

 

let sCss = "-webkit-border-radus";
let re = /-(\w)/g; 
let newCss = sCss.replace(re,function(a,b,c,d){
    return b.toUpperCase();
})
console.log(newCss); //WebkitBorderRadus

 

let sCss = "-webkit-border-radus";
let re = /-(\w)/g; 
let newCss = sCss.replace(re,function(res,child,index,origin){
    //第一个匹配到的index 坐标为0 
    return index? child.toUpperCase() : child
})
console.log(newCss); //webkitBorderRadus

 

小练习1: 给商品价格加上单位和币种

//小练习1 -- 给商品价格加上单位和币种
let str = "商品的价格是: 200"; //200
let re  = /\d+/;
let price = str.replace(re,"¥$&元");
console.log(price); //商品的价格是: ¥200元

 

小练习2:格式化日期

//使用子项进行日期格式化
let date = "2018-12-17";
//将date 替换成 12-17 2018
let re = /(\d{4})-(\d{1,2})-(\d{1,2})/g;
console.log(date.replace(re,'$2-$3 $1')); //12-17 2018

 

小练习3:给搜索出的关键字加上高亮效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>关键字高亮效果</title>
</head>
<body>
    <input type="text" id="txt">
    <button id="btn">搜索</button>

    <div id="content">
        乌克兰武装部队总参谋长维克多·穆真科(Viktor Muzhenko)称,“自2014年克里米亚事件以来,俄罗斯构成了‘军事威胁’。”他还称,卫星图像显示,在仅仅两周的时间内,至少有250辆俄罗斯坦克在边境附近集结。

维克多·穆真科甚至称,“很难预测俄罗斯会何时对乌克兰采取战斗行动”。

穆真科说,这些俄罗斯坦克是在刻赤海峡冲突事件后集结起来的,在这起冲突中,20名乌克兰海军军官被扣押,至今还未被释放。

此外,报道还提到,在刻赤海峡事件之后,乌克兰也希望其在亚速海附近军事基地的建设完工。
    </div>



    <script>
        let oTxt = document.getElementById("txt");    
        let oBtn = document.getElementById("btn");    
        let content = document.getElementById("content");
        let originContent = content.innerHTML;
        oBtn.onclick = function(){
            let value = oTxt.value.trim(); //获取输入的值,去掉左右空格
            let re = new RegExp(value,'ig');
            let sHtml = originContent.replace(re,"<span style='background: orange;'>$&</span>"); //在匹配的字符串前后加上带有背景颜色的span标签
            content.innerHTML = sHtml;
        }
    </script>
</body>
</html>

 

效果图如下:

 

用正则表达式替换真的很方便呢.

 

RegExp.$n  

n的值为1-9 ,还可以是下划线

$n 表示的是第几个子项,而子项是基于最近一次使用的正则表达式。

$_ 表示最近一次使用正则表达式 测试的字符串

如果没有子项则返回空字符串。

let str = "[email protected]"
let re = /\[email protected]\w+.[a-z]{2,4}/;
re.test(str); //使用正则表达式
console.log(RegExp.$1);// ""
console.log(RegExp.$_);// [email protected]
//为什么使用正则表达式还是空呢? 原来表达式里面根本没有子项

//重新定义有正则的子项
re = /(\w+)@(\w+).([a-z]{2,4})/;
console.log(RegExp.$1);// ""
console.log(RegExp.$_);// [email protected]
//为什么重新定义了还是空的? 原来是还没有重新使用正则,所以正则匹配的还是之前的表达式
re.test(str);
console.log(RegExp.$1);// 983521365
console.log(RegExp.$2);// 11
console.log(RegExp.$3);// com
console.log(RegExp.$_);// [email protected]

 

这个方法并非标准方法,而且基于最近一次使用正则表达式来判断的,若多人异步操作,可能导致结果出错,因此不建议投入到正式的使用当中。

 

子项的反向引用

 \n表示第几个子项的引用结果

 

let str = "213as426555aoqwu753333nmab8882138888pqwe9999820222";
//匹配出所有重复的数字
let re = /(\d)\1+/g;
console.log( str.match(re));// ["555", "3333", "888", "8888", "9999", "222"]

 

let str = "ads2a333o333ouoadsba666a666aoiwqeu999w9999owqe";
let re =  /((\d)\2+)\w\1/g;
console.log(str.match(re));//["333o333", "666a666", "999w999"]

 

小练习: 去掉html空白标签

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="content">
        <h2 class="title">跨越30年的两封信</h2>
        <h3 >     </h3>
        <h4>
            
        </h4>
        <div>
            <div class="content">

            昨天,有韩国媒体爆料了一起中国粉丝追星的“骚操作”:

            据称,有4名中国粉丝为了追星,先是买了跟偶像同一班的头等舱机票,在成功见到偶像后突然要求下飞机并全额退款。并且,他们还成功了……

            但这却导致机上360名乘客被迫重新安检,航班严重延误。

</div>
        </div>
        <p></p>
        <p>这是一段测试内容</p>
        <span></span>
    </div>
    
    <script>
        let sHtml = document.getElementById("content").innerHTML;
        let re = /<(\w+)><\/\1>/g;
        console.log(sHtml.match(re)); // ["<p></p>", "<span></span>"]
        //改进之后,让他能够匹配到空白字符
        let re2 =  /<(\w+)>[\n\t\r\s]*<\/\1>/g;
        console.log(sHtml.match(re2)); //["<h3> </h3>", "<h4>↵            ↵        </h4>", "<p></p>", "<span></span>"]
    </script>
</body>
</html>

 

 

零宽断言

  匹配结果不占长度,断言的判断内容不会被输出

  正先行断言 aaa(?=xxx) aaa紧接该位置之后必须存在xxx才会匹配
  负先行断言 aaa(?!xxx) aaa紧接该位置之后不存在xxx才会匹配
  正后行断言 (?<=xxx)aaa aaa紧接该位置之前必须存在xxx才会匹配
  负后行断言 (?<!xxx)aaa aaa紧接该位置之前不存在xxx才会匹配

 

let str = "word2013,excel2013,qq2018,yy2016";
//找到带2013把版本的软件,但是只显示文件名,不显示文件版本
let re = /\w+(2013)/g;
console.log(str.match(re));//["word2013", "excel2013"] 不满足需求
//使用正先行断言
let re2 = /\w+(?=2013)/g; //右边必须有2013,但是2013不会被输出
console.log(str.match(re2)); // ["word", "excel"]

//找到版本号不属于2016的软件
let re3 = /[a-z]+(?!2016)/g;
// console.log(str.match(re3));// 负先行断言 ["word", "excel", "qq", "y"] //不满足

let re4 = /[a-z]+(?=\d+[^2016])/g; //正先行断言 +范围排除
console.log(str.match(re4)); //["word", "excel", "qq"]

//匹配出yy的版本号
let re5 = /[a-z]{2}(?<=[yy])\d+/g;
console.log(str.match(re5)); //["2016"]

//匹配除了yy之外的其他软件版本号
let re6 = /(?<![yy])\d{4}/g;
console.log(str.match(re6)); //["2013", "2013", "2018"]