js之正则

330 阅读4分钟

    正则(regular expression)描述了一种字符串的匹配模式。一般应用于方法中,用一些特殊符号去代表特定的内容。对字符串中信息实现查找,替换和提取等操作。js中的正则表达式用RegExp对象表示。

一、定义正则表达式

字面量创建方式      /正则表达式/[修饰符可写可不写] 

实例创建方式       new RegExp(字符串,[修饰符])

let reg = /\d+/g;
reg = new RegExp("\\d+", "g");

    二者区别在于字面量定义的正则不能进行字符串拼接,实例创建方式可以进行字符串拼接,正则表达式中部分内容是变量存储的值。如果正则中要包含某个变量值,那么就不能使用字面量创建方式,因为字面量创建方式,两个斜杠包起来的都是元字符。而要使用构造函数创建,因为它传递的规则是字符串,字符串可以进行拼接。

        let str = "davina";
        reg = /^@"+str+"@$/;
        console.log(reg.test("@davina@")); //false
        console.log(reg.test('@"""strrrrr"@')); //true

        reg1 = new RegExp("^@" + str + "@$");
        console.log(reg1.test("@davina@")); //true

修饰符(放在第二个斜杠后,可写多个)

  • i: 不区分大小写,在确定匹配项时忽略大小写

  • m: 多行模式,在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项

  • g: 代表全局(global)匹配,模式将用于所有字符串,并非在发现第一个匹配项时就停止

二、常用的正则字符

1、元字符(常用)

表达式意义
\d数字(0-9之间的任意一个数字)
\D除数字以外的任意字符
\w数字,字母,下划线([0-9 a-z A-Z])
\W非数字,字母,下划线
\s空格
\S非空格
\\反斜杠
\'单引号
.任意一个字符(代表所有)
\.真正的点
\n换行
// 元字符:^ 以哪一个字符开始 $ 以哪个字符结束
console.log(/^\d+$/.test('2021my')); //false

// .不是小数点,而是基于\n外的任意字符 可以用\进行转义
console.log(/^2\.3$/.test('2.3')); //true

// x|y :x或y中的字符,它一般存在优先级问题,可以用小括号进行分组
console.log(/^18|29$/.test('182')); //true
console.log(/^(18|29)$/.test('182')); //false

2、中括号

表达式意义
[abc]查找中括号中的任意字符
[^abc]查找任何不在中括号之间的字符
[0-9]查找任何从0-9的数字
[a-z]查找从小a到小z的字符
[A-Z]查找从大A到大Z的字符
[\u4e00-\u9fa5]中文区间
// []一般代表本身的含义,它里面不存在多位数
let reg2 = /^[10-29]$/;  //=>代表1或0或2或9
console.log(reg2.test("1"));//=>true
console.log(reg2.test("9"));//=>true
console.log(reg2.test("0"));//=>true
console.log(reg2.test("7"));//=>false
console.log(reg2.test("10"));//=>false 

3、量词

表达式意义
*至少重复0次,最多不限{0,}(0到多)
+至少重复1次,最多不限{1,}(1到多)
?至少重复0次,最多重复1次{0,1}(0次,1次都有可能)
{n}重复n次(正好次)
{n,m}重复n到m次(至少重复n,最多重复m)
{n,}至少重复n,最多不限(n到多)
n$匹配任何结尾是n的字符串
^n匹配任何开头是n的字符串
x|y匹配x或者是y

4、分组

表达式意义
{}分组

    把单独的项组合成子表达式,以便可以像处理一个独立单元那样使用 |、*、+或者?等来对单元内的项进行处理

let str = "130828199012040112";
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;
console.log(reg.exec(str)); //["130828199012040112", "130828", "1990", "12", "04", "1"]

    如果即要捕获到数字,也想单独的把数字获取,可以用到如下方法:

    let str = "{2021}年{11}月{11}日",
        reg = /\{(\d+)\}/g,
        All_ary = [],
        Single_ary = [];
    res = reg.exec(str);
    while (res) {
        let [All, Single] = res;
        All_ary.push(All);
        Single_ary.push(Single);
        res = reg.exec(str);
    }
    console.log(All_ary, Single_ary); //["{2021}", "{11}", "{11}"] (3) ["2020", "11", "11"]

三、正则常用方法

1、RegExp对象相关方法

  • test()
    • 作用:查看正则表达式与指定字符串是否匹配,一般用于判断。它类化于字符串中的search()方法,差别在于test返回布尔值而search返回的是1/-1。
    • 语法:正则.test(字符串);
    • 返回值:返回true/false
let str = 'hello world';
console.log(/^hello$/.test(str)); //true
  • exec()
    • 作用:在一个指定字符串中执行搜索匹配。与match类似。但是它可以对单个字符串中多次匹配结果进行逐条遍历而match只会返回匹配到的结果
    • 语法:正则.exec(字符串)
    • 返回值:数组或者是null
let reg = /ab*/g;
let str = 'tttadfdfjaddadabbtrttw';
console.log(reg.exec(str));//["a", index: 3, input: "tttadfdfjaddadabbtrttw", groups: undefined]

2、String对象相关方法

  • match()
    • 作用:匹配指定的字符串或者是正则,把匹配到的结果放到一个数组中
    • 语法:字符串.match(字符串或者正则)
    • 返回值:找到后把结果放在数组且返回,没有找到返回null
    • 注意:如果不带g修饰符,只会匹配一个结果,且给找到的数组增加两个属性:index(找到字符对应的位置),input(原字符串,从哪个字符串身上找)。
    • exec()和match()的区别:1. 一个是正则方法一个是String方法,并且exec()只会匹配第一个符合的字符串(g对其并不起作用),而match是否返回所有匹配的数组和正则里是否带g有关。
    let str = 'avada33fkdja123fas22gj34qw3r';
    let reg1 = /\d+/g;
    console.log(reg1.exec(str)[0]); // 33
    console.log(str.match(reg1)); //["33", "123", "22", "34", "3"]

  • search()
    • 作用:找到匹配的字符串首次出现的位置
    • 语法:字符串.search(字符串或者正则)
    • 返回值:找到了返回位置的下标,没有找到返回-1
    • 注意:search()方法参数可以是正则,indexOf()方法参数不能是正则
    let str = 'davina123Davian';
    let re = /\d/;
    console.log(str.indexOf(re), str.search(re));//-1 6
  • split()
    • 作用:按约定字符串或者字符串规则拆分成数组,接受一个字符串或者正则
    • 语法:字符串.split(字符串或者正则)
    • 返回值:数组
    let color = '1 red green 2';
    console.log(color.split(' ')); //["1", "red", "green", "2"]
    console.log(color.split((/\s/), 3)) //["1", "red", "green"]
  • replace()方法
    • 作用:替换匹配的字符串
    • 语法:str.replace(regexp|substr,newSubStr|function)
    • 参数:regexp它是一个正则或者是字面量,该正则所匹配的内容会被第二个参数的返回值替换;substr:一个将被 newSubStr 替换的 字符串,仅第一个匹配项会被替换;newSubStr用于替换掉第一个参数在原字符串中的匹配部分的字符;function用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。
    • 返回值:替换后的新字符串,原来的字符串没有任何变化。要强调一点使用这个函数一定要有返回值,否则会用undefined来替代
    //使用字面量进行替换
    let str = ' apple apple APPLE ';
    let r1 = str.replace('a', 'A');
    console.log(r1); // Apple apple APPLE  只替换第一个匹配的字符

    let r2 = str.replace(/a/gi, 'Aa');
    console.log(r2); // Aapple Aapple AaPPLE 使用正则可以全部进行替换,且后面的参数可以是字符串形式

    //交换字符串中两个单调的位置
    let reg2 = /(\w+)\s(\w+)/;
    let str1 = 'tom mark';
    console.log(str1.replace(reg2, '$2,$1')); //mark,tom

四、捕获

    正则捕获具有贪婪性和懒惰性。所谓贪婪性就是正则在捕获时,每一次都会尽可能多地去捕获符合条件的内容。懒惰性则指正则在成功捕获一次后不管后面字符串是否符合条件都不再捕获。默认情况下正则匹配是贪婪匹配,如果想要取消则只需在量词元字符后面设置?就可。

    对于懒惰性而言,exec()方法中就用到了这一特性。它捕获的结果是null或者是一个数组,每执行一次exec(),只能捕获到一个符合正则规则的结果,当我们执行100遍,获取到的结果永远都是第一个匹配到的结果,这就是正则捕获的懒惰性引起的,默认只捕获第一个。     懒惰性产生的原因是因为默认情况下lastIndex(当前正则下一次匹配起始索引位置)的值不会被修改,每一次都是从字符串开始的位置查找,所以找到的永远只是第一个。我们可以用全局修饰符g来解决这个问题。设置全局匹配修饰符g后,第一次匹配完,lastIndex会自己修改,当全部捕获完后,再次捕获的结果为null,lastIndex又回到了初始值,再次捕获又从第一个开始。

    let str = "lily2019lily2020";
    let reg = /\d+/
    let reg1 = /\d+?/g;

    console.log(str.match(reg)); //["2019", index: 4, input: "lily2019lily2020", groups: undefined]
    console.log(str.match(reg1)); //["2", "0", "1", "9", "2", "0", "2", "0"]

五、常用正则

每一个单词的首字母大写


    let str = "keep on going never give up.";
    let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;
    
    str = str.replace(reg, (...arg) => {
        let [content, $1] = arg;
        $1 = $1.toUpperCase();
        content = content.substring(1);
        return $1 + content;
    });
    console.log(str); //Keep On Going Never Give Up.

验证是否为有效数字

// +/-号可能出现可能不出现;当一位时0-9都可多位时首位不是为0;小数可有可无,一旦有小数点,后面必须要有数字
let reg = /^[-+]?(\d|([1-9]\d+))(\.\d?)$/;

千分符&url处理$时间字符串格式化

  ~(function () {
        //1.千分符
        /*
         * thousandMark:实现数字的千分符处理
         * @params:
         * @return
         *  [string] 千分符后的字符串
         */
        function thousandMark() {
            return this.replace(/\d{1,3}(?=(\d{3})+$)/g, (content) => content + ",");
        }


        //2.url的处理
        /*
         * queryURLParams:获取url地址问号后面的参数信息(可能有hash值)
         * @params
         * @return
         *   [object] 把所有问号参数信息以键值对的方式存储起来且返回
         */
        function queryURLParams() {
            let obj = {},
                reg = /([^?=&#]+)=([^?=&#]+)/g;
            this.replace(reg, (...[, $1, $2]) => (obj[$1] = $2));
            this.replace(/#([^?=&#]+)/g, (...[, $1]) => (obj["hash"] = $1));
            return obj;
        }

        //3.时间字符串的格式化处理
        /*
         * formatTime:时间字符串的格式化处理
         *   @params
         *  templete:[string] 我们最后希望获取的日期格式的模板
         */
        function formatTime(templete = "{0}年{1}月{2}日 {3}时{4}分{5}秒") {
            let timeAry = this.match(/\d+/g);
            return templete.replace(/\{(\d+)}/g, (...[, $1]) => {
                let time = timeAry[$1] || "00";
                return time.length < 2 ? "0" + time : time;
            });
        }

        /*  把所有的方法扩展到内置类String.prototype上 */
        ["thousandMark", "queryURLParams", "formatTime"].forEach((item) => {
            String.prototype[item] = eval(item);
        });
    })();

    //进行验证
    let num = "1234553423423"; //1,234,553,423,423
    console.log(num.thousandMark());

    let url = "http://www.davina.cn/?lx=1&from=qq#video";
    console.log(url.queryURLParams()); //{lx: "1", from: "qq", hash: "video"}

    let time = "2020-8-13 16:1:39";
    console.log(time.formatTime()); //2020年08月13日 16时01分39秒