写点什么

还不会正则表达式? 放心 我会出手(万字教学)

  • 2022-11-22
    河北
  • 本文字数:5543 字

    阅读完需:约 18 分钟

体验正则表达式的能力

在一堆字符串中,获取里面的数字。

  普通代码

  let hd = 'jianyidexiaoxietongzhi2548564';    let numstr = [...hd].filter(item => !Number.isNaN(parseInt(item)));    console.log(numstr);
复制代码

  正则表达式代码

  let hd = 'jianyidexiaoxietongzhi2548564';    console.log(hd.match(/\d/g));
复制代码

创建正则表达式

  使用字面量的方式

    优点

方便快捷,便于书写使用。

//建立一个字符串变量let hd = 'jianyidexiaoxietongzhi';//字面量创建正则let rex = /j/;//test 是一个正则方法,判断正则表达式是否能在字符串中匹配到内容,返回值是布尔值console.log(rex.test(hd));
复制代码

    缺点

正则表达式只可以匹配表面的字符串。获取不到变量

//建立一个字符串变量let hd = 'jianyidexiaoxietongzhi123';//字面量创建正则let j = '1';let rex = /j/;//match 是一个字符串方法,前面的字符串包不包含扩内的的正则表达式,包含的话则返回值,没有的话返回nullconsole.log(hd.match(rex));
复制代码

获取到的是变量 j 字符串,而不是变量 j 里的具体内容。


使用 eval 方法和模板字符串解决问题不大

//建立一个字符串变量let hd = 'jianyidexiaoxietongzhi123';//字面量创建正则let j = '1';let rex = `/${j}/`;//使用模板字符串获得变量的值,在使用eval获取返回值console.log(hd.match(eval(rex)));
复制代码



  使用对象创建正则表达式

//不需要写/ /直接写正则内容即可let hd = 'jianyidexiaoxietongzhi'//使用对象的方式创建正则let reg = new RegExp('d', 'g');console.log(hd.match(reg));
复制代码

对象创建的正则也可以直接识别变量,注意使用对象创建的时候 写相关标识符的时候比如\d 需要写成\ \d 写两个\才有效果,字符串里面一个\只代表一个普通文本。

let hd = 'jianyidexiaoxietongzhi'//使用对象的方式创建正则let d = 'j'let reg = new RegExp(d, 'g');console.log(hd.match(reg));
复制代码

方法

  正则表达式的两个方法

    test

判断正则表达式中的内容是否包含在字符串中,无论在什么位置,返回值是逻辑值,true 或 false

let str = /xiaoxie/;console.log(str.test('jianyidexiaoxietongzhi'));
复制代码



    exec

从字符串中获取符合正则表达式规则的部分片段,返回值是捕获的字符串等相关内容,没有捕获到则返回 null。

      没有捕获到

let str = /xiaoxie/;console.log(str.exec('jianyidexaoxietongzhi'));
复制代码



      捕获到

let str = /xiaoxie/;console.log(str.exec('jianyidexiaoxietongzhi'));
复制代码

返回捕获到的字符串 以及字符串开始的索引位置,和原字符串,只会匹配到第一次符合规则的字符串。



特殊字符

  基础元字符

\s

表示匹配一个空格字符,等价于‘ ’。

let rex = /\s/console.log(rex.test('   d'));
复制代码



\S

表示匹配一个非空格字符,字符串里面需要至少一个非空格字符

let rex = /\S/;console.log(rex.test('   x'));
复制代码



\t

表示匹配一个 tab 字符,字符串里面需要至少一个 tab 字符,

let rex = /\t/;//两个空格console.log(rex.test('  '));//一个tab建console.log(rex.test('	'));
复制代码



\d

表示匹配数字 0 到 9,字符串必须包含一个数字。

let rex = /\d/;console.log(rex.test('dsad5'));
复制代码



\D

表示匹配一个非数字,字符串中必须包含一个非数字内容。

let rex = /\D/;console.log(rex.test('45645s45612'));
复制代码



\w

表示匹配数字,字母,下划线,三种字符。

let rex = /\w/;console.log(rex.test('sd45_'));
复制代码



\W

表示匹配非数字,字母,下划线以外的三种字符。

let rex = /\W/;console.log(rex.test('sd45_'));
复制代码



.

表示非换行字符,有换行\n 以外的字符就返回 true。

let rex = /x/;console.log(rex.test('\n'));
复制代码



\

表示转义符号,将有意义的转无意义的内容,无意义的转有意义的内容。

let rex = /\./;//点原本表示非换行符,通过转义符 变成了匹配一个.。console.log(rex.test('.'));
复制代码



  边界元字符

用来控制开头结尾内容的元字符。

^

表示匹配字符的开头内容。

let rex = /^xiao/;//返回false   开头需要是xiao而不是xxiao。console.log(rex.test('xxiaoxie')); 
复制代码



$

表示匹配字符的结尾内容

//表示开头到结尾 为两个数字let rex = /^\d\d$/;console.log(rex.test('13')); 
复制代码



  限定符

写在元字符,或者普通字符后面,用来限定次数的符号。

*

表示需要匹配到 0 到多个指定字符。

//表示需要匹配到任意个数字字符串。let rex = /^\d*$/;console.log(rex.test('154544'));   trueconsole.log(rex.test(''));    true
复制代码

+

表示需要匹配到 1 到多个指定字符。

let rex = /^\d+$/;console.log(rex.test(''));  falseconsole.log(rex.test('123'));  true
复制代码

?

表示需要匹配到 0 到一次指定字符。

//字符串中需要有0到1个数字。let rex = /\d?/;console.log(rex.test('sdfsf'));    trueconsole.log(rex.test('sdf123sf')); true
复制代码

{n}

表示需要匹配到指定次数的指定字符。

//连续五个数字字符串let rex = /\d{5}/;console.log(rex.test('sddsg458'));   falseconsole.log(rex.test('sddsg45458'));   true
复制代码

{n,}

表示需要匹配到 n 到多次的指定字符。

let rex = /\d{5,}/;console.log(rex.test('12d24545'));   trueconsole.log(rex.test('12d245'));   false
复制代码

{n,m}

表示需要匹配到 n 到 m 次指定字符串。

let rex = /\d{2,3}/;//是匹配的内容 而不是控制字符串中数字的内容,需要匹配到至少两个数字,只有一个则返回false,有五个数字,则从里面匹配三个数字,返回true。console.log(rex.test('1ds'));   falseconsole.log(rex.test('45465'));  true
复制代码

  贪婪和非贪婪

    贪婪

当我们使用限定符,捕获字符的时候,它会尽可能的从多捕获,这就是正则的贪婪性。

let rex = /\d{1,5}/console.log(rex.exec('45684'));
复制代码



    非贪婪

我们只需要在限定符后面再加个?就是非贪婪限定符了,它会优先选择最少的而不是多的。

let rex = /\d{1,5}?/console.log(rex.exec('45684'));
复制代码



 小练习

    练习 1

      需求

捕获第一个 div 的完整标签。

      代码

<!-- 我们可以通过贪婪限制符号,来获取标签,这是一种常见的场景。不加?的话,则.会尽可能获取多的内容,左右<>会匹配最外面两边的尖括号。加上贪婪限制符之后,则会选择最少的内容并且达成<>的条件。 -->let str = '<div style=" color : "red""></div>'
let rex = /<.+?>/
console.log(rex.exec(str));
复制代码



    练习 2


let rex = /abcd{2}/;console.log(rex.test('abcddjhgfff')); true<!-- 上面题目中,表示的是两个d 也就是abcdd,而不是abcabc 如果字符串中包含abcdd则返回true -->
复制代码

  元字符——特殊符号

()

被()括起来的内容,表示一个整体,可以整体括起来进行输出。

<!-- 括起来表示一个整体 所以是匹配两次括号里的内容,也就是需要字符串内包含abcdabcd -->let rex = /(abcd){2}/
console.log(rex.test('dssdabcdabcd')); true

复制代码

除此之外,还会单独捕获括号内的内容,从左边开始的每一个小括号里的内容都是从索引 1 开始的数组内容,索引 0 为匹配到的内容

<!-- 索引0为匹配到的结果abcdefg索引1为左边开始的第一个小括号 ab索引2为左边第二个小括号cdefg索引3为左边第三个小括号de -->let rex = /(ab)(c(de)fg)/console.log(rex.exec('abcdefghijklmn'));
复制代码



(?😃

和()的区别就是,这个只有整体效果,不会捕获括号内的数据变成索引内容

let rex = /(?:ab)(?:c(?:de)fg)/console.log(rex.exec('abcdefghijklmn'));
复制代码



|

表示或者,会匹配满足的一项内容。

let rex = /123|456/;console.log(rex.exec('212456'));
复制代码



[]

表示匹配中括号内的任意一项

let rex = /[abcd]/;console.log(rex.exec('graedfsab'));
复制代码



[^]

表示匹配非中括号内的任意一项

let rex = /[^abcd]/;console.log(rex.exec('graedfsab'));
复制代码



[n-m]

表示 n 到 m 之间的内容,n 到 m 之间的内容实际上是之间的 ascll 码值。

let rex = /[0-9]/;console.log(rex.exec('g2raedfsab'));
复制代码



\num

表示前面的某个括号内的东西,重复出现一遍,/1 这个数字 1 不是指的是出现几次,而是指的是第几个小括号内的结果重复来一遍

let rex = /(abc|def)\d+\1/;console.log(rex.test('dsabc123abc'));     trueconsole.log(rex.test('dsabc123def'));     false
复制代码

  组合形式

[09a-zA-Z_] 等于 \w[^09a-zA-Z_] 等于 \W[0-9] 等于 \d[^0-9] 等于 \D[ ] 等于 \s[^ ] 等于 \S注意:当.放在[]中只代表一个.,相当于加了转义符 
复制代码

  标识符

i

表示忽略大小写

let rex = /[abcdef]/i;console.log(rex.test('ABC'));   true
复制代码

g

表示全局查找,或许应该叫动态查找,跟动态变量一样,下次的查找会遵循上一次查找的位置继续查找,如果查找不到了就返回 null,还继续查找的话,会从头开始。对于字符串 match 方法加了 g,则会一次性返回所有匹配内容

let rex = /\d{1}/gconsole.log(rex.exec('123'));console.log(rex.exec('123'));console.log(rex.exec('123'));console.log(rex.exec('123'));console.log(rex.exec('123'));console.log(rex.exec('123'));let rex = /es(?=2015|2016)/glet str = 'es2015es2016es2017'console.log(str.match(rex));
复制代码



y

粘性全局跟 g 的区别就是粘性全局不能间隔捕获,也就是第一个匹配的内容必须从【0】开始就捕获到,第二个内容必须从第一个内容的结束位开始就捕获到,如果没有捕获到就返回 null,下次捕获就从头开始。

let rex = /\d{1}/yconsole.log(rex.exec('123'));console.log(rex.exec('1g2g3'));console.log(rex.exec('123'));console.log(rex.exec('12g3'));console.log(rex.exec('123'));
复制代码



 小练习

   练习 1

let rex = /^(abc|def){2}$/;console.log(rex.test('abcabc'));  trueconsole.log(rex.test('defdef'));  trueconsole.log(rex.test('abcdef'));  trueconsole.log(rex.test('defabc'));  true
复制代码

   练习 2

验证一个字母字符串,只能由数字字母下划线组成,6 到 12 位,不能以_开头。

let rex = /^[a-zA-Z0-9]\w{5,11}$/;console.log(rex.test('ads54s_'));console.log(rex.test('_ads54s'));console.log(rex.test('%ads54s'));
复制代码

    练习 3

正则验证数字,验证 0-255

思路:把 0-255 数字分成几类一位数   \d 两位数   \d{2}1 开头的三位数   1\d{2}2 开头的第二位是 0-4 的三位数   2[0-4]\d2 开头的第二位是 5 的第三位为 0-4 的三位数   2[5]\d

let rex = /^(\d|\d{2}|1\d{2}|2[0-4]\d|25[0-5])$/修改后let rex = /^(1?\d{1,2}|2[0-4]\d|25[0-5])$/
console.log(rex.test('0454441')); falseconsole.log(rex.test('234')); trueconsole.log(rex.test('123')); trueconsole.log(rex.test('25622')); false
复制代码

    练习 4

邮箱匹配

邮箱 @前的字符为 6 到 10 位,不能以下划线开头,只能由数字字母下划线组成,只能是 qq 网易 163 邮箱,后缀只能是.com 或.cn

let rex = /^[0-9a-zA-Z]\w{5,9}@(qq|163)(.com|.cn)$/;console.log(rex.test('22175451@163.com')); true
复制代码

正则的预查

  正向预查

   正向肯定

(?=)

捕获某个内容的时候,可以增添条件,比如后面需要连接什么,例如,我想吃泡面,吃老坛酸菜的,泡面是内容,老坛酸菜是条件

<!-- 匹配es2015或者es2016,这种符合要求的,匹配到了之后,得到es。也就是说   我要拿到es,但是我要拿到es2015或者2016里面的es -->let rex = /es(?=2015|2016)/;console.log(rex.exec('es2015'));console.log(rex.exec('es2016'));console.log(rex.exec('es2017'));
复制代码



   正向否定

(?!)

捕获某个内容的时候,可以增添条件,比如后面不需要连接什么,例如,我想吃泡面,不吃老坛酸菜的,泡面是内容,不吃老坛酸菜是条件

let rex = /es(?!2015|2016)/;console.log(rex.exec('es2015'));console.log(rex.exec('es2016'));console.log(rex.exec('es2017'));
复制代码



  负向预查

   负向肯定

跟正向肯定一样,区别就是,负向的肯定的条件在前面。

(?<=)

let rex = /(?<!2015|2016)es/;console.log(rex.exec('2015es'));console.log(rex.exec('2016es'));console.log(rex.exec('2017es'));
复制代码



let rex = /(?<=2015|2016)es/;console.log(rex.exec('2015es'));console.log(rex.exec('2016es'));console.log(rex.exec('2017es'));
复制代码



   负向否定

跟正向否定一样,区别就是,负向的否定的条件在前面。

 let rex = /(?<!2015|2016)es/; console.log(rex.exec('2015es')); console.log(rex.exec('2016es')); console.log(rex.exec('2017es'));
复制代码



  字符串与正则合作的几个方法

    search()

这个方法就是查找匹配对象的索引位置,只返回索引位置。

let rex = /a/let str = 'dsa';console.log(str.search(rex));    2
复制代码

    replace()

使用正则表达式替换字符串,没有\g 的时候只会替换第一个匹配的,有\g 则替换掉所有匹配符合的内容。

let str = '11111'console.log(str.replace(/1/g, 'a'));console.log(str.replace(/1/g, function (data) {    return Number(data) + 1;}));
复制代码



    match()

跟 exec 类似,区别就是正则表达式为 g 的时候会匹配所有符合的内容

let rex = /es(?=2015|2016)/glet str = 'es2015es2016es2017'console.log(str.match(rex));
复制代码




    search()

这个方法就是查找匹配对象的索引位置,只返回索引位置。

let rex = /a/let str = 'dsa';console.log(str.search(rex));    2
复制代码

    replace()

使用正则表达式替换字符串,没有\g 的时候只会替换第一个匹配的,有\g 则替换掉所有匹配符合的内容。

let str = '11111'console.log(str.replace(/1/g, 'a'));console.log(str.replace(/1/g, function (data) {    return Number(data) + 1;}));
复制代码



    match()

跟 exec 类似,区别就是正则表达式为 g 的时候会匹配所有符合的内容

let rex = /es(?=2015|2016)/glet str = 'es2015es2016es2017'console.log(str.match(rex));
复制代码




发布于: 刚刚阅读数: 4
用户头像

还未添加个人签名 2022-10-14 加入

还未添加个人简介

评论

发布
暂无评论
还不会正则表达式? 放心 我会出手(万字教学)_正则表达式_坚毅的小解同志_InfoQ写作社区