让你学懂正则表达式(1)

342 阅读7分钟

让你学懂正则表达式(1)

作者:GrayRabbit 时间:2021.11.3

概述

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。——百度百科

简单说正则表达式就是一个对于字符串匹配规则,但是不同于大家所普遍认为的匹配字符串只是对字符匹配,其实正则表达式也可以对位置进行匹配(有些时候甚至更重要?)。如果还迷糊,不要急,下面灰兔将详细介绍字符与位置的匹配方法。

字符匹配

1.精确匹配

/javascript/

上面的正则表达式可以精确的匹配一个字符串中的“javascript”子串。但是如果正则表达式只能进行这种匹配似乎并没有多厉害呀?没错,正则表达式的强大之处在于它可以模糊匹配。

2.模糊匹配

在说模糊匹配之前先介绍两个东西量词字符组

量词:用来描述某个字符(字符组)重复出现的次数的规则。

字符组:用来描述可以匹配的字符的合集的规则。

我们可以很直观的可以感受到,量词是用来横向的向前拓展,而字符组是用来纵向的展开。那么我们应该如何使用呢?

首先介绍量词最基本的写法{n,m},其表示匹配前面的字符(字符组)最少n次,最多m次。例如:

/^a{2,6}$/
a ×
aa √
aaaaaa √
aaaaaaa ×

这个正则表达式,它表示匹配a最少2次,最多6次,其中^$代表这个字符串的前面就是开头和这个字符串的接下来就是结尾(也就是说字符串整体匹配),下面会详细介绍,这里先了解就行。

量词还有其他的形式:

  1. {n}:匹配精确n次。
  2. {n,}:匹配至少n次,没有上限。
  3. {n,m}:匹配最少n次,最多m次。
  4. ?:匹配0或1次,作用等同于{0,1}。
  5. *:匹配至少0次,没有上限,作用等同于{0,}。
  6. +:匹配至少1次,没有上限,作用等同于{1,}。
  7. 前面的规则紧着加个?,如+?,{2,6}?,表示惰性匹配,即匹配达到最小次数就开始匹配下个字符。(默认情况是贪婪的)

没有{,m}这种写法!!!!!!!

量词只作用于前一个字符或者前面一个规则(括号,字符组等),而不是前面整个字符串!!!!!

几个例子:

/^ab?$/

a √ ab √ abb × b最多可以重复1次

/^ab+$/

a × b至少要重复一次 ab √ abbbbbbb √

/^a(ab){1,2}$/

aab √ aabb × 要“ab”整体重复 aabab √

好了,我们已经学会了横向的拓展,下面我们来使用字符组来进行纵向的展开。

还是介绍最基本的写法[abc],它代表匹配a或者b或者c。让我们配合前面的量词来个综合练习:

/^[abc]{2,}$/
a x   至少匹配2次,且每次为a或b或c
ac √
abc √
aaaaabbbbbcccc √
abcd ×  只能匹配a或者b或者c,有d不符合规则

这个正则表达式匹配这样字符串,它开头与结尾之前是由a或者b或者c组成的长度至少为2的字符串。

字符组还有下面这些形式:

  1. [char1-char2] : 表示匹配char1和char2以及它们之间的字符,如[1-5]等于[12345],[a-d]等于[abcd],[A-D]等于[ABCD]。
  2. 除了1情况下的字符组中的- :表示匹配 - 这个字符,如/[abc-]{2,}/可以匹配ab-c,------,abc-等。
  3. [^] : 开头加个^ 表示反向范围(注意^和[紧挨着),如[^a-z] 可以匹配除小写字母外的字符。
  4. \d :等于[0-9]。
  5. \D :等于*[^0-9]*。
  6. \w :等于[0-9a-zA-Z_],匹配数字、大小写字母和下划线。
  7. \W :等于[^0-9a-zA-Z_]。
  8. \s : 等于[ \t\v\n\r\f],匹配空格、水平制表符、垂直制表符、换行符、回车符、换页符。
  9. \S :等于[^\t\v\n\r\f]。
  10. . : 等于[^\n\r\u2028\u2029]。通配符,匹配几乎任意字符。
  11. [^] :真正的匹配所有字符。
/^[a-c]{2,}$/

a x 至少匹配2次,且每次为a或b或c ac √ abc √ aaaaabbbbbcccc √ abcd × 只能匹配a或者b或者c,有d不符合规则

/^\d+$/

123 √ 12312a ×只能匹配数字

/^[^a-z]+$/

123 √ 12312a ×有小写字母a

3.分支匹配

如果同一个部分有多种可能的规则怎么办呢,那么就要用到分支匹配了。

具体形式如下:p1|p2|p3 ,其中 p1、p2 和 p3 是子模式,用 |分隔,表示匹配其中任何之一。比如

/^(\d+\.\d+|\d+)$/
42.1 √  匹配\d+\.\d+ 即数字小数点数字
43 √ 匹配\d+
43a × 全都不匹配

4.小作业:

  1. 写一个正则表达式,它可以匹配颜色的 #rgb 这种形式,如#fff #Acb #e3e3e3。
  2. 写一个正则表达式,它可以匹配 yyyy-mm-dd 这种格式的日期。
  3. 写一个正则表达式,它可以匹配 xx:xx 格式的时间。

位置匹配

1.什么是位置?

大部分人在利用正则表达式时,可能会忽略位置匹配,位置其实就是字符与字符之间的间隙,比如上面遇到的^代表的就是字符串第一个字符前的位置,$就是字符串最后一个字符后面的位置,下图红色箭头指的都叫做位置,用来匹配位置的叫做

image-20211103205052551.png

2.怎么匹配位置

一共有8种锚,除了上文说的^$,还有\b\B(?=p)(?!p)(?<=p)(?<!p)

下面我们来详细的介绍下:

1. ^ 起始 $ 结尾

^表示匹配单行模式下第一个字符的前面,多行模式下各行的开头。类似的,$表示匹配单行模式下最后个字符的后面,多行模式下各行的结尾。如:

"abcaa".replace(/^a/g,"123") === "123bcaa"
"abcaa".replace(/a/g,"123") === "123bc123123"
"abcaa".replace(/^bc/g,"123") === "abcaa"
"abcaa".replace(/bc/g,"123") === "a123aa"
"abcaa".replace(/a$/g,"123") === "abca123"
"abcaa".replace(/a/g,"123") === "123bc123123"
"abcaa".replace(/bc$/g,"123") === "abcaa"
"abcaa".replace(/bc/g,"123") === "a123aa"

第一个会匹配前面是开头的字符a,因此会匹配到第一个a,替换后就是"123bcaa"。

第三个虽然字符串中有bc,但前面并不是开头,所以不会被替换。

第五个会匹配后面是结尾的字符a,因此会匹配到最后一个a,替换后就是"abca123"。

第七个虽然字符串中有bc,但后面并不是开头,所以不会被替换。

2. \b 单词边界 \B非单词边界

\b表示\w和\W,^,$之间的位置。类似的\B表示\W和\w,^,$之间的位置,就是所有位置减去\b的位置。比如:

"hello world!".replace(/\b/g,"&") === "&hello& &world&!"
"hello world!".replace(/\B/g,"&") === "h&e&l&l&o w&o&r&l&d!&"
"hello world!".replace(/\bh/g,"&") === "&ello world!"
"hello world!".replace(/\b|\B/g,"&") === "&h&e&l&l&o& &w&o&r&l&d&!&"

第一二个不多解释,注意的是它们的合正好就是所有位置,4可以验证。

第三个表示字符边界位置后面还要紧跟着一个h,符合的显然就只有第一个字母h。

3. (?=p) 正向先行断言 (?!p) 负向先行断言 (?<=p) 正向后行断言 (?<!p) 负向后行断言

正向先行断言表示后面是p的位置,负向先行断言表示后面不是p的位置,正向后行断言表示前面是p的位置,负向后行断言表示前面不是p的位置。其中前两个的合是所有位置,后两个的合是所有位置。比如:

"hello world!".replace(/(?=l)/g, "&") === "he&l&lo wor&ld!"
"hello world!".replace(/(?!l)/g, "&") === "&h&ell&o& &w&o&rl&d&!&"
"hello world!".replace(/(?!l)|(?=l))/g, "&") = "&h&e&l&l&o& &w&o&r&l&d&!&"
"hello world!".replace(/(?<=l)/g, "&") === "hel&l&o worl&d!"
"hello world!".replace(/(?!l)/g, "&") === "&h&e&llo& &w&o&r&ld&!&"
"hello world!".replace(/(?<!l)|(?<=l))/g, "&") === "&h&e&l&l&o& &w&o&r&l&d&!&"

如果我们想把数字变成每三位用逗号分隔,就可以这样写:

"100000000".replace(/(?!^)(?=(\d{3})+$)/g, ",") === "100,000,000"

首先这个位置不能是开头,其次这个位置到结尾之前的数字的个数正好是3的大于等于1倍个。

3.位置的特性

字符之间的位置可以被无限选中,如:

"hello world!".replace(/^(?=h)/g, "&") === "&hello world!"

很明显,两个位置同时选中了开头的位置。

4.小作业

  1. 写一个正则表达式,使得可以用replace将一个句子 每个字母的开头前 加入一个符号。

  2. 写一个正则表达式,使得可以用replace将一个句子 每个字母的结尾后 加入一个符号。

结语

至此为止,我们已经可以利用正则表达式做绝大部分东西啦,可以关注下博主蹲下面的更新哦。

可以把作业交在评论区哦~灰兔也会在评论区公布答案。