• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

JS正则表达式。以vue词法、句法为例

武飞扬头像
依然范特西fantasy
帮助1

1,声明

其声明和其他的基本类型类似,都可以用字面量声明,或者包装类声明:

  1.  
    console.log(/abc/g); // /abc/g
  2.  
     
  3.  
    console.log(new RegExp("abc", "g")); // /abc/g

二者各有各的优势,字面量声明,更符合常规用法,而包装类可以更清晰的声明包含变量的正则表达式:

  1.  
    const matchStr = "matchStr";
  2.  
    console.log(new RegExp(matchStr, "g")); // /matchStr/g
  3.  
     
  4.  
    //但也不是说字面量声明就不可以包含变量,你可以这么做:
  5.  
    console.log(eval(`/${matchStr}/g`)); // /matchStr/g
  6.  
    //也将会被解析为一个正则表达式。eval会将其字符串参数转换为js表达式,且其上下文指向当前上下文

2,基本语法

或:

  1.  
    console.log(/ab|cd/.test("ab")); //true
  2.  
    console.log(/ab|cd/.test("cd")); //true

转义:

  1.  
    // 例如'/'为正则的边界符,如果需要在正则中使用该符号,需要这么做:
  2.  
    /\//;
  3.  
    //在包装类中,需要对转义符进行转义,才能正常使用,因此需要这样:
  4.  
    console.log(new RegExp("\\/")); // 最终会转译为:/\//
  5.  
    //常见的需要转义的字符如下:
  6.  
    /\/|\^|\||\$|\(|\)\<|\{|\}|\.|\*|\ |\?|\\/;

边界约束:

  1.  
    //起始边界约束
  2.  
    console.log(/^a/.test("ab")); // true
  3.  
    console.log(/^a/.test("ba")); // false
  4.  
    console.log(/a/.test("ba")); // true
  5.  
     
  6.  
    //结束边界约束
  7.  
    console.log(/a$/.test("ba")); // true
  8.  
    console.log(/a$/.test("ab")); // false
  9.  
    console.log(/a/.test("ab")); // true

元字符:

  1.  
    //数字与非数字:
  2.  
    console.log(/\d/.test("1")); // true
  3.  
    console.log(/\D/.test("1")); // false
  4.  
     
  5.  
    //大小写字母及下划线,与非大小写字母或下划线
  6.  
    console.log(/\w\w\w/.test("a_A")); // true
  7.  
    console.log(/\W/.test("a_A")); // false
  8.  
     
  9.  
    //空白和非空白(空白包括了换行、缩进、空格、制表等和空格相关的)
  10.  
    console.log(/\w\s\w\s\w\s\w/.test("b\ta\nc c")); // true
  11.  
    console.log(/\S/.test(" ")); // false
  12.  
     
  13.  
    //全匹配(除换行):
  14.  
    console.log(/./.test("a")); // true
  15.  
    console.log(/./.test(" ")); // true
  16.  
    console.log(/./.test("_")); // true
  17.  
    console.log(/./.test("&")); // true
  18.  
    console.log(/./.test("1")); // true
学新通

数量限制:

  1.  
    //零个或一个
  2.  
    console.log(/^\w?$/.test("")); // true
  3.  
    console.log(/^\w?$/.test("a")); // true
  4.  
    console.log(/^\w?$/.test("abc")); // false
  5.  
     
  6.  
    //任意数量(零个、一个、多个)
  7.  
    console.log(/^\d*$/.test("")); // true
  8.  
    console.log(/^\d*$/.test("1")); // true
  9.  
    console.log(/^\d*$/.test("123")); // true
  10.  
     
  11.  
    //一个或多个
  12.  
    console.log(/^\s $/.test("")); // false
  13.  
    console.log(/^\s $/.test(" ")); // false
  14.  
    console.log(/^\s $/.test("\n\t")); // false
  15.  
     
  16.  
    //自定义数量
  17.  
    console.log(/^\d{1,2}$/.test("")); // false
  18.  
    console.log(/^\d{1,2}$/.test("1")); // true
  19.  
    console.log(/^\d{1,2}$/.test("12")); // true
  20.  
    console.log(/^\d{1,2}$/.test("123")); // false
  21.  
    //若省略,则代表无穷多
  22.  
    console.log(/a{1,}/.test("aaaaaaaaa")); // true
  23.  
    console.log(/a{1}/.test("aaaaaaaaa")); // true
  24.  
     
  25.  
    //禁止贪婪
  26.  
    //在上述语法中,每一项的匹配都是贪婪的,也就是会尽可能多的匹配到符合自身要求的字符
  27.  
    //禁止贪婪则能让其在符合条件的基础上,尽可能少的去匹配字符
  28.  
    console.log("aaababab".match(/^(\w*)b/));
  29.  
    //['aaababab', 'aaababa', index: 0, input: 'aaababab', groups: undefined]
  30.  
    console.log("aaababab".match(/^(\w*?)b/));
  31.  
    //['aaab', 'aaa', index: 0, input: 'aaababab', groups: undefined]
学新通

原子组

原子组主要是配合match方法使用。match在匹配正则的时候,若没有显式的在原子组内声明"?:",则会将原子组内的匹配内容捕获,如:

console.log("abcacb".match(/.(acb)/)[1]); // acb

在一个正则表达式中,可以使用任意数量的原子组,可以将每一个原子组的内容都捕获:

  1.  
    console.log("abcacbcccddd".match(/abc(acb)(ccc)(ddd)/));
  2.  
    // ['abcacbcccddd', 'acb', 'ccc', 'ddd', index: 0, input: 'abcacbcccddd', groups:undefined]

如果在原子组内声明了"?:",则会忽略对其的捕获:

  1.  
    console.log("abcacbcccddd".match(/abc(acb)(ccc)(?:ddd)/));
  2.  
    // ['abcacbcccddd', 'acb', 'ccc', index: 0, input: 'abcacbcccddd', groups: undefined]

这种写法并非多次一举,比如当你不确定某些整体的内容是否会出现时,你可能需要使用原子组将其变为一个整体,再使用" ? "或者" * ",但是你并不关心他的结果,不需要捕获,如:

  1.  
    console.log("abcabcwanted".match(/(?:abc)*(wanted)/));
  2.  
    //  ['abcabcwanted', 'wanted', index: 0, input: 'abcabcwanted', groups: undefined]

原子表:

如果使用了原子表,则只需要符合表内任意一种表达式个体,匹配结果就为true,类似于或语法:

console.log(/[abcd]*/.test("dacb")); // true

但是需要注意的是,原子表会将正则拆分成最小的个体,比如在此例中的abcd,就不再是"abcd"了,而是"a"|"b"|"c"|"d"

console.log(/^[abcd]$/.test("abcd")); // false

断言匹配:

  1.  
    //匹配内容之前必须为XXX
  2.  
    console.log(/(?<=limit)abc/.test("limitabc")); // true
  3.  
    console.log(/(?<=limit)abc/.test("limabc")); // false
  4.  
     
  5.  
    //匹配内容之后必须为XXX
  6.  
    console.log(/abc(?=limit)/.test("abclimit")); // true
  7.  
    console.log(/abc(?=limit)/.test("abclim")); // false
  8.  
     
  9.  
    //匹配内容之前不能是XXX
  10.  
    console.log(/(?<!limit)abc/.test("limitabc")); // false
  11.  
    console.log(/(?<!limit)abc/.test("limabc")); // true
  12.  
     
  13.  
    //匹配内容之后不能是XXX
  14.  
    console.log(/abc(?!limit)/.test("abclimit")); // false
  15.  
    console.log(/abc(?!limit)/.test("abclim")); // true
学新通

模式修正符:

常用的几个模式修正符如下所示:

i 表示在和模式进行匹配进不区分大小写
m 将模式视为多行,使用^和$表示任何一行都可以以正则表达式开始或结束
s 如果没有使用这个模式修正符号,元字符中的"."默认不能表示换行符号,将字符串视为单行
g 全局匹配,本意是对字符串进行完整匹配 

以上只是对正则表达式的语法做了粗浅的介绍,正则表达式在不同组合、不同模式下会产生不同的效果,仅仅纸上谈兵是不够的,都需要自己多手动尝试。

3,基本练习

1,获取字符串中的数字字符,并以数组形式输出。如:12ak3222ljfl444223ql99kmf678,输出:[12, 3222, 444223, 99, 678]。

  1.  
    const reg = /(\d )/g;
  2.  
     
  3.  
    console.log("12ak3222ljfl444223ql99kmf678".match(reg));
  4.  
    //['12', '3222', '444223', '99', '678']

2,将字符串转换为驼峰形式。

  1.  
    const reg = /(-\w)/g;
  2.  
     
  3.  
    console.log(
  4.  
    "get-element-by-id".replace(reg, (match) => {
  5.  
    return match.slice(1).toUpperCase();
  6.  
    })
  7.  
    );
  8.  
    //getElementById

3,和谐字符校验,输入的字符不能包含和谐字符 "csdn"。要求:校验必需使用断言

  1.  
    const reg = /^(?!.*csdn.*)[\w\W]*/g; // /[\w\W]*(?<!.*csdn.*)$/g 也同样可以
  2.  
     
  3.  
    console.log(reg.test("acsdnbc"));

4,github提交规范校验。要求:feat(1234 section): Added new features 或 fix(222 section): fixed issue

  1.  
    const reg = /^(feat|fix)\((?:(\d )\s ([^\(\)] ))\):\s*([\w\W]*)$/;
  2.  
    console.log("fix(123 section): make some bug".match(reg));
  3.  
    // ['fix(123 section): make some bug', 'fix', '123', 'section', 'make some bug', index: 0, input: 'fix(123 section): make some bug', groups: undefined]

4,初窥vue源码中的正则表达式

假设你对正则表达式已经有了基本的了解,对其特性也略知一二。现在将会带你以vue中的词法分析、句法分析中出现的正则表达式为例,对其进行逐个分析:

首先是对attribute的解析:

  1.  
    const attribute =
  2.  
    /^\s*([^\s"'<>\/=] )(?:\s*(=)\s*(?:"([^"]*)" |'([^']*)' |([^\s"'=<>`] )))?/;

乍一看会很绕,但是没关系,我们将其拆开来看:

  1.  
    const a = /^\s*/; // 以0~n任意空格数开头
  2.  
    const b = /([^\s"'<>\/=] )/; // 匹配不为原子表中的这几个字符的字符串
  3.  
    const c = /(?:\s*(=)\s*(?:"([^"]*)" |'([^']*)' |([^\s"'=<>`] )))?/;
  4.  
    //第三个比较复杂,再将其拆分来看
  5.  
    const c1 = /\s*(=)\s*/; //匹配"="
  6.  
    let c2 = /(?:"([^"]*)" |'([^']*)' |([^\s"'=<>`] ))/;
  7.  
    //c2又分为以下三个部分:
  8.  
    const c21 = /"([^"]*)" /;
  9.  
    // 匹配双引号包裹的内容,匹配方法用到的是双引号中不为双引号的任意字符,且处理了末尾多引号的情况
  10.  
    const c22 = /'([^']*)' /;
  11.  
    // 匹配单引号包裹的内容,匹配方法用到的是单引号中不为单引号的任意字符,且处理了末尾多引号的情况
  12.  
    const c23 = /([^\s"'=<>`] )/;
  13.  
    // 匹配除了原子表中的内容,实际上就是不被单双引号包裹的value
  14.  
     
  15.  
    c2 = c21 || c22 || c23;
  16.  
    //再将其组装起来,
  17.  
    //c2:匹配单引号,双引号,或不使用引号的内容
  18.  
    //c:匹配 等号 加上 c2内容,且其整体是可以不存在的(用了?))
学新通

因此,其最终的匹配结果为如下几种情况:

class="some-class"、class='some-class'、class=some-class、disabled;

然后是匹配dynamicArgAttribute:

  1.  
    const dynamicArgAttribute =
  2.  
    /^\s*((?:v-[\w-] :|@|:|#)\[[^=] ?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)" |'([^']*)' |([^\s"'=<>`] )))?/;

依旧还是很长,同样的,我们拆分着看:

  1.  
    const dynamicArgAttribute =
  2.  
    /^\s*((?:v-[\w-] :|@|:|#)\[[^=] ?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)" |'([^']*)' |([^\s"'=<>`] )))?/;
  3.  
    const a = /^\s*/; // 依旧是经典开头
  4.  
    let b = /((?:v-[\w-] :|@|:|#)\[[^=] ?\][^\s"'<>\/=]*)/;
  5.  
    //其中包括三个部分,继续拆分:
  6.  
    const b1 = /(?:v-[\w-] :|@|:|#)/;
  7.  
    //匹配v-开头,加上任意数量的字母、下划线、-;或@:# 。即v-[model|text|on|directives] or @ or : or #
  8.  
    const b2 = /\[[^=] ?\]/;
  9.  
    //匹配[]内的不为等号的1~n的字符,对应着v-model[dynamicName],动态属性名
  10.  
    const b3 = /[^\s"'<>\/=]*/;
  11.  
    //匹配不为上述内容的任意数量的字符,对应着修饰符,v-model[dynamicName].modified
  12.  
    b = b1 b2 b3;
  13.  
    //因此,b匹配的内容应该为 v-bind[dynamicName].modified
  14.  
     
  15.  
    let c = /(?:\s*(=)\s*(?:"([^"]*)" |'([^']*)' |([^\s"'=<>`] )))?/
  16.  
    //继续拆分
  17.  
    const c1 = /\s*(=)\s*/
  18.  
    //匹配等号及其前后的空白
  19.  
    const c2 = /"([^"]*)" |'([^']*)' |([^\s"'=<>`] )/
  20.  
    //这个在之前已经讲到过了,也就是匹配单引号,双引号,或不使用引号的内容
  21.  
    //并且,因为c末尾处有符号?,其整体可以为0或1
学新通

因此,其最终的匹配结果为:

v-bind[dynamicName].sync = "value"

其中,[ ] 及其内容是必须存在的,而指令符可以为v-开头或者几种缩写形式,修饰符可以为0~n,等号及其后面的内容可以为0或1;

上述两个是在词法分析中比较经典的案例,接下来再来看句法分析中的几个正则表达式:

forAliasRE:

  1.  
    const forAliasRE = /([\s\S]*?)\s (?:in|of)\s ([\s\S]*)/;
  2.  
     
  3.  
    const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;

从变量名中不难看出,这两个正则是用来处理v-for指令的。但是和词法分析相比,长度上来看,已经易读许多了。但我们依旧将其拆分来看:

  1.  
    const forAliasRE = /([\s\S]*?)\s (?:in|of)\s ([\s\S]*)/;
  2.  
     
  3.  
    const a = /([\s\S]*?)/
  4.  
    //匹配任意字符,且禁止贪婪(表达式的意思是空白或非空白,二者都可,那么就代表为任意字符全匹配)
  5.  
    //同样的 [\d\D] [\w\W] 也都具有一样的效果。这种写法与 . 相比,可以多匹配换行符
  6.  
    const b = /\s (?:in|of)\s /
  7.  
    //匹配前后1~n任意数量的空白,内容为 in 或 of
  8.  
    const c = /([\s\S]*)/
  9.  
    //依旧是一个全匹配

因此,其最终匹配的结果为:(以 v-for = "(item,key,index) in arr" 为例)

(item,key,index),arr。而of和in不参与捕获。

而下一个正则 forIteratorRE 正是处理第一个参数的:

  1.  
    const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/;
  2.  
    //匹配 "," 之后,不为原子表中的字符,且匹配一次或两次

最终,其会将第一个参数(item,key,index) 的后两个字段加上其之前的逗号都给捕获:

,key 、,index

至于为什么这么做,可以在代码中略知一二:

  1.  
    const iteratorMatch = alias.match(forIteratorRE);
  2.  
     
  3.  
    res.iterator1 = iteratorMatch[1].trim();
  4.  
     
  5.  
    if(iteratorMatch[2]) {
  6.  
    res.iterator2 = iteratorMatch[2].trim();
  7.  
    }
  8.  
     
  9.  
    res.alias = alias.replace(forIteratorRE, "").trim();

最终,vue会将v-for的第一个参数,解析为:

  1.  
    {
  2.  
    alias:'item'
  3.  
    iterator1:'key'
  4.  
    iterator2:'index'
  5.  
    }

最后,再来看看slot属性的正则:

const slotRE = /^v-slot(:|$)|^#/;

这是一个很简单的正则表达式,在此就不做分析,留给大家独立思考。

正则是一个比较常见,且很实用的工具。其语法相对枯燥,但是掌握好正则,绝对会在日常开发中提供大用处。

文中内容均带有个人理解,并不保证权威。若有错误,欢迎随时批评指正。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgeihia
系列文章
更多 icon
同类精品
更多 icon
继续加载