# (十一) 学习正则表达式的简单方法

正则表达式是繁琐的,但它是强大的,学会之后的应用会让你除了提高效率外,会给你带来绝对的成就感。

# 一、什么是正则?

  1. 正则表达式是一种被用于从文本中检索符合某些特定模式的文本。
  2. 正则表达式可以被用来替换字符串中的文本、验证表单、基于模式匹配从一个字符串中提取字符串等等。

先来看看一个经典场景:

想象一下,您希望在用户填写用户名时遵守以下规则,仅包含字母,数字,下划线和连字符。同时我们还想限制用户名中的字符数量。这时我们可以使用以下正则表达式来验证用户名:

在这里插入图片描述

下面的代码就是上面正则表达式的简单应用

<body>
  <input type="text" id="input" />
  <button onclick="test()">验证</button>
  <script>
    const oInput = document.getElementById("input");
    const reg = /^[0-9a-z_-]{3,15}$/;
    function test() {
      const e = oInput.value;
      if (e.match(reg)) {
        window.alert("用户名验证通过");
      } else {
        window.alert("⚠️验证错误⚠️");
      }
    }
  </script>
</body>

上面的规则组合是我们根据目标字符串的特点分析出来的,接下来我们来看看它的基本语法吧

# 1.1 test() 检测字符串

test() 方法用于检测一个字符串是否匹配某个模式。

RegExpObject.test(string)

返回值:

如果字符串string中含有与RegExpObject匹配的文本,则返回true,否则返回false

示例:

/abc/.test('123'); // false
/abc/.test('abcd'); // true

# 1.2 replace() 替换字符串

replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

返回值:

一个新的字符串,是用 replacement 替换了 regexp 的第一次匹配或所有匹配之后得到的。

示例:

'yuguang up'.replace(/yuguang/, '余光'); // "余光 up"

# 二、元字符

元字符是正则表达式的基本组成元素。元字符在这里跟它通常表达的意思不一样,而是以某种特殊的含义去解释。

常用的元字符如下:

元字符 描述 举例
. 匹配除换行符以外的任意字符 /.ing/.test('eating')
[xyz] 字符集,匹配方括号中包含的任意字符 '[abc]' 可以匹配 "plain" 中的 'a'
[^xyz] 负值字符集合。匹配未包含的任意字符 负值字符集合。匹配未包含的任意字符。例如,
* 匹配前面的子表达式零次或多次 /abc*/.test('ab') 匹配 0 次是可以的
+ 匹配前面的子表达式零次或多次 /abc*/.test('abcc')
? 匹配前面的子表达式零次或一次 /123?/.test('12')
{n,m} 花括号,匹配前面字符至少 n 次,但是不超过 m 次
(xyz) 字符组,按照确切的顺序匹配字符 xyz
| 分支结构,匹配符号之前的字符或后面的字符
\ 转义符,它可以还原元字符原来的含义,允许你匹配保留字符
^ 匹配行的开始
$ 匹配行的结束

# 2.1 英文句号

英文句号,在匹配时也与它实际意义不同,它是元字符中最简单例子.

我们要匹配 "End" 这个字符串,还要仅仅运用 ".",可以怎么做?

const reg = /.nd/
reg.test('End'); // true

# 2.2 字符集 和 否定字符集

字符集又叫字符类,通常情况下它表示,匹配大括号内所包含的任意一个字符

我们要匹配 "End"或"end" 时,可以怎么做?

const reg = /[Ee]nd/
reg.test('End'); // true
reg.test('end'); // true
reg.test('and'); // false

**注意1:**元字符^代表从字符串的起始位置开始匹配,但在字符集内,它表示否定或取反

// 注意1:
const reg = /[^Ee]nd/
reg.test('End'); // false
reg.test('end'); // false
reg.test('and'); // true

**注意2:**元字符.代表换行符以外的任意字符,但在字符集内,它就代表英文句号

// 注意2:
const reg = /ok[.]/
reg.test('is ok!'); // false
reg.test('is ok.'); // true

# 2.3 重复

重复通常是匹配某一个规则多次,这和花括号的部分作用重合,区别在于匹配的次数问题

# 2.3.1 星号

星号(*)表示匹配上一个匹配规则零次多次

const reg = /yuguang*/
reg.test('yuguan'); // true
reg.test('yuguanggggg'); // true

# 2.3.2 加号

加号(+)表示匹配上一个匹配规则1次多次

const reg = /yuguang+/
reg.test('yuguang'); // true
reg.test('yuguan'); // false

# 2.3.3 问号

问号(?)用来表示前一个字符是可选的。该符号匹配前一个字符零次或一次。

表示:可选的字母y紧跟小写字母uguang

const reg = /y?uguang/
reg.test('uguang'); // true
reg.test('yuguang'); // true

# 注意

*如果出现在字符集之后,它表示整个字符集的重复。

const reg = /[0-9]*/
reg.test('12345'); //true

# 2.4 花括号

在正则表达式中花括号用于指定字符或一组字符可以重复的次数。例如正则表达式 [0-9]{2,3},表示:匹配至少2位数字但不超过3位

  • 我们可以省略第二个数字:则正则表达式 [0-9]{2,},表示:匹配 2 个或更多个数字。
  • 我们也可以删除逗号:则正则表达式 [0-9]{2},表示:匹配正好为 2 位数的数字。
const reg = /[0-9]{1,}/
reg.test(''); //false
reg.test('1'); true

# 2.5 字符组

字符组是一组写在圆括号内的子模式 (...),(xyz)按照确切的顺序匹配字符x->y->z

let str = 'ababcdcd';
str.replace(/ab/, '替换'); // "替换abcdcd"
str.replace(/(ab)*/, '替换'); // "替换cdcd"

注意:

正如我们看到的,把量词放在一个字符组之后,它会重复整个字符组。 例如正则表达式 (ab)* 表示匹配零个或多个的字符串“ab”。

# 2.6 分支(或)

在正则表达式中垂直条 | 用来定义分支结构,分支结构就像多个表达式之间的条件。现在你可能认为这个字符集和分支结构的工作方式一样。 但是字符集和分支结构巨大的区别是字符集只在字符级别上有作用,然而分支结构在表达式级别上依然可以使用。 例如正则表达式 (T|t)he|car,表示:匹配大写字母 T 或小写字母 t,后面跟小写字母 h,后跟小写字母 e,或匹配小写字母 c,后跟小写字母 a,后跟小写字母 r。

const reg = /(T|t)he|car/
reg.test('The'); // true
reg.test('tcar'); // true

# 2.7 转义特殊字符

正则表达式中使用反斜杠 \ 来转义下一个字符。这将允许你使用保留字符来作为匹配字符:

{ } [ ] / \ + * . $ ^ | ?

let reg = /\.{1}/
'aa.bb.cc'.replace(reg, '-'); // "aa-bb.cc"

# 2.8 定位符

在正则表达式中,为了检查匹配符号是否是起始符号或结尾符号,我们使用定位符。

# 2.8.1 插入符号

插入符号 ^ 符号用于检查匹配字符是否是输入字符串的第一个字符。

// 因为起始不是a->b->c 替换失败
let reg = /^abc/
'not abc'.replace(reg, '***'); // "not abc"

# 2.8.2 $符号

美元 $ 符号用于检查匹配字符是否是输入字符串的最后一个字符。

// 因为起始不是a->b->c 替换失败
let reg = /abc$/
'not abc'.replace(reg, '***'); // "not ***"

# 三、简写字符集

正则表达式为常用的字符集和常用的正则表达式提供了简写。简写字符集如下:

简写 描述
. 匹配除换行符以外的任意字符
\w 匹配所有字母和数字的字符:[a-zA-Z0-9_]
\W 匹配非字母和数字的字符:[^\w]
\d 匹配数字:[0-9]
\D 匹配非数字:[^\d]
\s 匹配空格符:[\t\n\f\r\p{Z}]
\S 匹配非空格符:[^\s]

注意:这里并不是全部的简写,仅列出了常用或常见的。

我们依次为表格中的简写配一个示例

匹配除换行符以外的任意字符:

// 将换行符前的内容打码
'hi yuguang \n hi xiaoming'.replace(/.+/, '***'); // "***\n hi xiaoming"

匹配所有字母和数字的字符:

'123'.replace(/\w/, '***'); // ***

匹配数字:

// 匹配存在连续3位数字的字符串
/\d{3}/.test('1a2b3c444'); // true

匹配空格符:

// 将空格替换成 :
'12 59'.replace(/\s/, ':'); // "12:59"

# 四、断言

编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,而正则中的断言也是类似的存在:

先来看看都有哪些断言

符号 描述
?= 正向先行断言
?! 负向先行断言
?<= 正向后行断言
?<! 负向后行断言

# 4.1 正向先行断言

正向先行断言认为第一部分的表达式的后面必须是先行断言表达式。返回的匹配结果仅包含与第一部分表达式匹配的文本。

let reg = /[Tt]he(?=\syuguang)/
reg.test('the yuguang'); // true
reg.test('the xiaoming'); // false

表示:匹配T或t,->h->e。 在括号中,我们定义了正向先行断言,它会引导正则表达式引擎匹配后面跟着 yuguang 的 The 或 the。

# 4.2 负向先行断言

当我们需要指定第一部分表达式的后面不跟随某一内容时,使用负向先行断言。

let reg = /[Tt]he(?!\syuguang)/
reg.test('the xiaoming'); // true

# 4.3 正向后行断言

不难理解,后行就是匹配规则在之后的,用于获取跟随在特定模式之后的所有匹配内容。

例如正则表达式 (?<=(T|t)he\s)(fat|mat),表示:从输入字符串中获取在单词 The 或 the 之后的所有 fat 和 mat 单词。

let reg = /(?<=(T|t)he\s)(fat|mat)/
'The fat cat'.replace(reg, '***'); // "The *** cat"

# 4.4 负向后行断言

获取跟随在特定模式以外的所有匹配内容

let reg = /(?<=(T|t)he\s)(fat|mat)/
reg.test('the fat cat'); // false
// 任意个的数字 + . | 数字 + 任意个数字
/(\d+(\d|\.)\d+)(\w)/

# 五、修饰符

不知道大家对上面的例子是否存在疑问,比如

  1. 匹配一段规则,可以做到全局替换吗?
  2. 如果换行了怎么办?
  3. ...

这就又引出了修饰符:因为它会修改正则表达式的输出。这些标志可以以任意顺序或组合使用,并且是正则表达式的一部分。

标记 描述
i 不区分大小写:将匹配设置为不区分大小写。
g 全局搜索:搜索整个输入字符串中的所有匹配。
m 多行匹配:会匹配输入字符串每一行。

# 5.1 不区分大小写

i 修饰符用于执行不区分大小写匹配。

'Hello'.replace(/H/i, '*'); // "*ello"

# 5.2 全局搜索

g 修饰符用于执行全局匹配(会查找所有匹配,不会在查找到第一个匹配时就停止)

'112233-112233-112233'.replace(/(112233)/g, '密码'); // "密码-密码-密码"

# 5.3 多行匹配

m 修饰符被用来执行多行的匹配。正如我们前面讨论过的 (^, $),使用定位符来检查匹配字符是输入字符串开始或者结束。但是我们希望每一行都使用定位符,所以我们就使用 m 修饰符。

'The fat\n cat sat\n on the mat'.replace(/.(at)$/gm, '结尾');
// "The 结尾\n cat 结尾\n on the 结尾"

# 六、常见正则练习

# 6.1 电话号码匹配

像电话号码的匹配规则其实没有绝对正确的答案,先来分析一下下面正则要匹配的手机号应该符合什么样的规则?

正则:

`/^1([358][0-9]|4[579]|6[67]|7[0135678]|9[189])[0-9]{8}$/`

分析:

  1. 第一位一定是1
  2. 第二位+第三位:要根据括号内的分支结构区分。分别是:
  • 第二位:3、5、8 => 第三位:任意数字;
  • 第二位:4 => 第三位:5、7、9;
  • 第二位:6 => 第三位:6、7;
  • 第二位:7 => 第三位:0、1、3、5、6、7、8;
  • 第二位:9 => 第三位:1、8、9;
  1. 剩余八位:任意8位数组排列组合`

手机号正则表达式虽然很长,但实际分析起来并不复杂,在知道确切的规则后相信大家都能慢慢推导出来,话不多说直接上图:

图示:

在这里插入图片描述

# 6.2 邮箱校验

正则:

/^([a-zA-Z0-9_\-]+)@([a-zA-Z0-9_\-]+)(\.[a-zA-Z0-9_\-]+)$/

分析:

  1. 起始:任意大小写字母或下划线或或数字 + @
  2. 中间:任意大小写字母或下划线或或数字 + .
  3. 结束:任意大小写字母或下划线或或数字

图示:

在这里插入图片描述

# 6.3 xxxx-xx-xx日期转换

有这样一个需求,明确的知道时间格式是:“2021-1-1”和“2021-12-1”,要准确的提取出年、月、日需要怎么做的?(只考虑正则哦)

这次我们先分析后写正则:

分析

  1. 年:固定四位数字:^[0-9]{4}
  2. 月:1-2位数字:
  • 1位时:[1-9]
  • 2位时:1[0-2]
  1. 日:1-2位数字:
  • 1位时:[1-9]
  • 2位时:[12][0-9]|3[01]$

正则

//      $1           $2                $3
const reg = /^([0-9]{4})-([1-9]|1[0-2])-([12][0-9]|3[01]|[1-9])$/

// 测试代码
const [,year, month, day] = '2012-1-12'.match(reg)

图示:

在这里插入图片描述

# 写在最后

  • 花名:余光
  • WX:j565017805
  • 邮箱:webbj97@163.com

# 其他沉淀

Last Updated: 2/14/2023, 2:43:41 PM