正则表达式学习与整合

正则表达式

正则表达式是在处理字符串时,非常便利的工具

而正则表达式本身是由一些特殊的字符组成特定的规则, 去匹配字符串, 可以做到 验证, 替换, 获取 的功能, 对应一些复杂的字符串处理更是得心应手

菜单

参考文档

正则表达式: https://www.baidufe.com/item/eb10deb92f2c05ca32cf.html

replace 解析: https://www.cnblogs.com/lvmylife/p/9079297.html

分组: https://blog.csdn.net/zcdyx88/article/details/54016551

这些也是我在学习过程中,自己总结的内容, 如果有缺失或错误, 欢迎指出

使用方式

最开始先熟悉 使用方式, 这样在后续学习中就可以 边做边学了

正则表达式在 js 环境中, 是有以下几种 声明方式使用方式

//声明方式
1. let reg = /正则表达式/修饰符; // /[a-z]/g

2. let reg = new RegExp("正则表达式","修饰符"); // new RegExp("[a-z]","g")

3. let reg = new RegExp();
   reg.compile("正则表达式","修饰符"); // 重新定义正则表达式

//使用方式
1. reg.test("字符串"); // 返回 true,false

2. reg.exec("字符串"); // 返回 匹配内容的信息

3. "字符串".match(reg); // 返回 匹配字符的数组 或 内容的信息

4. "字符串".matchAll(reg); // reg必须带修饰符g, 返回 匹配内容信息的数组

每个使用方式 的结果都不一样, 但最后的目的是一样的, 就是验证获取 匹配字符串的信息

刚开始如果对这些概念不熟悉可以先记忆

额外说明

使用方式execmatch 在没有修饰符的情况下, 作用是一样的

声明方式2 3 其实是没区别的, 要说区别的话, 可能第 3 种在循环遍历 不同的表达式 的情况下会快一些, 因为不需要重新声明 new RegExp, 就可以改变 正则表达式 的内容

声明方式修饰符 是可以省略的, 但 matchAll 必须带 g

替换

替换的 使用方式和 上面大同小异, 用的是 replace()

例子

//格式
"字符串".replace(正则表达式 / RegExp, "替换文本");

//例子
"abc".replace(/[a-z]/g, "b"); // "bbb"

"abc".replace(new RegExp("[a-z]", "g"), "b"); //bbb

替换 空字符 的话就可以实现删除的效果了, 替换常用的还有 正则的 分组 功能, 熟悉一下即可

如果对该方法不熟悉或不懂, 可以浅尝即止(看看例子知道有什么作用就行), 然后去看 组成部分 的内容

分组替换

在替换字符串的时候, 有时会有这种需求.

比如: 只是想在 指定字符串 的附近加点东西,不想改变 原有的内容, 但用上面的 替换 就会导致原内容被替换 而无法实现需求

这时分组替换就 有用了

//例子
"abc".replace(/([a-z])/g, "_$1"); //_a_b_c

"abc".replace(/([a-z])([a-z])/g, "_$1*$2"); //_a*bc

"a_bc".replace(/\_([a-z])/g, (all, letter) => {
  return letter.toUpperCase();
}); // aBc

"a_b_c".replace(/\_([a-z])\_([a-z])/g, (all, letter, letter2) => {
  return letter.toUpperCase() + letter2.toUpperCase();
}); // aBC

其作用就是 使用 正则表达式 的分组, 来实现 单组匹配内容 的占位, 之后就可以使用 占位符 来进行替换, 有多少分组, $数字占位符 数字就可以是多少, 而 占位符 的地方 会嵌入原有的匹配内容 这样就实现了需求

replace 的第二个参数 为函数, 其参数列表按顺序 分别是

  • 匹配的内容
  • 分组的匹配内容(无分组可省略, 有分组时则有几个是几个)
  • 匹配字符串位置索引
  • 原内容

额外说明

replace() 第二个参数 占位符 其实还有其他很多种,每种都有不同的作用

//$& 与正则相匹配的字符串
"abc".replace(/b/, "_$&_"); //a_b_c

//$` 匹配字符串左边的字符
"abc".replace(/b/, "_$`_"); // a_a_c

//$' 匹配字符串右边的字符
"abc".replace(/b/, "_$'_"); //a_c_c

这些替换是不需要分组就可以使用的, 和普通的替换一样, 只不过用法和分组 占位符 差不多

组成部分

正则表达式 的 组成是有规则的, 不同情况下 组合不同的规则写法, 就能实现各个功能

我会提供一些例子, 和对部分难理解的内容 进行额外说明, 为了 结果直观明显, 例子我都会使用 替换 的形式 进行操作

例子都可以直接复制到 F12 开发工具里面测试, 有疑问也可以进行调整

元字符

字符对应说明
[]匹配括号内部所有的单个字符, [0-9],[a-z],[A-Z],\ 转义字符 除外
.匹配除换行符之外的任意字符
\w匹配字母数字下划线,等同于:[a-zA-Z0-9_]
\s匹配任意空白符
\d匹配数字,等同于[0-9]
\b匹配单词边界
|或匹配,如 /x|y/ 正则可匹配 x 或 y 两个字符
^匹配字符串的开始
$匹配字符串的结束

例子

//[]
"1abcABC".replace(/[a-z]/g, "x"); //1xxxABC
"1abcABC".replace(/[0-9]/g, "x"); //xabcABC
"1abcABC".replace(/[A-Z]/g, "x"); //1abcxxx
"1abcABC".replace(/[0-9a-zA-Z]/g, "x"); //xxxxxxx
"1abcABC".replace(/[a-b]/g, "x"); //1xxcABC
"1.abcABC".replace(/[.]/g, "x"); //1xabcABC
"1\nabc".replace(/[\x0a]/g, "x"); //1xabc

// . (换行符是指\n)
"a\nbc".replace(/./g, "b"); //b\nbb

// \w
"abc123_".replace(/\w/g, "b"); //bbbbbbb

// \s
" abc ".replace(/\s/g, "b"); //babcb

// \d
"1abc2".replace(/\d/g, "b"); //babcb

// \b
"abc abc".replace(/\b/g, "b"); //babcb babcb

// |
"abcd".replace(/a|c/g, "b"); //bbbd

// ^
"abcd".replace(/^/g, "b"); //babcd

// $
"abcd".replace(/$/g, "b"); //abcdb 下面接着用
"abcdb".replace(/$/g, "b"); //abcdbb

通过上面的例子, 有人就会发现 有些规则并不只是匹配具体内容, 还会去匹配位置, 比如 \b, ^, $, 而当你将其替换时, 就会在指定位置添加, 但不代表这个位置之后就匹配不了了, 像这种位置的 替换 是可以重复使用的

注意 [] 内部所有的字符都会当做匹配内容处理, 转义字符(\)除外 , 所以在括号内部无法使用规则, 而 [0-9],[a-z],[A-Z] 这三个是 约定好的规则, 所以 其中的三个字符 不会被当做 匹配内容处理, 而且这三个规则可以同时使用 [0-9a-zA-Z], 且范围也可以更改 [1-3b-dB-D]

反义字符

字符对应说明
[^x]匹配除“x”之外的所有字符,其中“x”可以为任意字符
[^xyz]同上,匹配除“x、y、z”之外的任意字符
\W匹配除了字母、数字、下划线之外的所有字符,等同于:[^\w]
\S匹配除空白符之外的任意字符,等同于:[^\s]
\B匹配不是单词边界的字符,等同于:[^\b]
\D匹配不是数字的所有字符,等同于:[^\d]

反义字符 从字面意思 就是取反, 简括就是 除了这个,其他的都可以

反义字符 其实是有固定格式的 [^内容] , 而 元字符\小写字母 规则, 也可以通过 \大写字母 来实现反义, 这样是不是容易记忆一点

额外说明

\大写字母 仅限于 元字符 的反义, 转义字符 可不是, 因为 转义字符 本质上只是 特殊字符的 简写, 不算 匹配规则 比如 你在 F12 输入 字符串 “\x0c”, 就会得到 “\f”

如果要使用 转义字符 的反义, 应该使用 [^x], 比如 [^\f][^\x0c]

转义字符

字符对应说明
\f匹配换页符,等同于:\x0c
\n匹配换行符,等同于:\x0a
\r匹配回车符,等同于:\x0d
\t匹配水平制表符,等同于:\x09
\v匹配垂直制表符,等同于:\x0b
\unnnn匹配 Unicode 字符,如:\u00A0
\xnn匹配十六进制数 如: \x48

其中 n 代表任意数字字母, 写在这里的意思是 \u 的后 4 个 任意数字字母 都会匹配到一起, \x 同理

至于 Unicode 字符, 十六进制数 的意思就不在这里过多 赘述了, 有兴趣的可以查阅相关 文档

// \unnnn
"\u00A0".replace(/\u00A0/, "a"); // a
"\u00A0".replace(/\u00A01*/, "a"); // a

// \xnn
"\x48".replace(/\x48/, "a"); // a
"\x48".replace(/\x481*/, "a"); // a

一般来说 转义字符 用的情况会比较少, 只需记住即可, 记不住的在用到时也可以回来查阅

重复匹配

字符对应说明
*重复出现零次或多次
+重复出现一次或多次
重复出现零次或一次
{n}重复出现 n 次
{n,}至少重复出现 n 次
{n,m}重复重现 n 到 m 次,其中,n<m

例子

// *
"abc".replace(/[a-z]*/g, "b"); //bb

// +
"abc".replace(/[a-z]+/g, "b"); //b

// ?
"abc".replace(/[a-z]?/g, "b"); //bbbb

// {n}
"abc".replace(/[a-z]{1}/g, "b"); //bbb

// {n,}
"abc".replace(/[a-z]{1,}/g, "b"); //b

// {n, m}
"abc".replace(/[a-z]{1,2}/g, "b"); //bb

重复匹配是很重要的 规则, 基本上复杂一点的 正则都会使用, 需要掌握熟练

贪婪与惰性

其实在使用 重复匹配 时, 会发现 匹配数量是有范围的, 且数量偏向于 大值 比如 {1,2} 优先 匹配 2 个的字符, 而不是 1 个, 而让匹配数量偏向于 小值 就要用到 贪婪与惰性

重复匹配 的规则后 加个 ? 就是 贪婪与惰性

"abc".replace(/[a-z]+?/g, "b_"); // b_b_b_
"abc".replace(/[a-z]{1,2}?/g, "b_"); // b_b_b_

//小值为0时
"abc".replace(/[a-z]{0,2}?/g, "b_"); // b_ab_bb_cb_
"123".replace(/[a-z]{0,2}?/g, "b_"); // b_1b_2b_3b_

当小值为 0 时, 就会去匹配字符的 间隙 位置, 而不会去匹配 规则内容 (因为匹配以 0 长度为准), 这也是一个特殊的使用方式,当你想在每一个字符之间插入某些内容时就 可以这么做

分组/捕获

字符对应说明
(exp)捕获分组, 匹配 exp
(?<name>exp)捕获分组, 匹配 exp, 并将该匹配内容放到 name 的组里
(?:exp)不捕获分组, 匹配 exp 正则
exp1(?=exp2)不捕获分组, 匹配 exp1,但后面必须是 exp2
exp1(?!exp2)不捕获分组, 匹配 exp1,但后面不能是 exp2
(?<=exp2)exp1不捕获分组, 匹配 exp1,但前面必须是 exp2
(?<!exp2)exp1不捕获分组, 匹配 exp1,但前面不能是 exp2

分组是用于 分别处理多个匹配内容的最佳手段, 通常和 replace 进行搭配使用, 来实现一次匹配,不同修改

被分组的内容 会被当成一个整体, 整体受到其他规则的影响

不捕获分组 简单来讲是指不会产生 分组占位符, 也就是 $1,$2,$3... 或者 $<name>

例子

//分组使用
"abc".replace(/([a-z])([a-z])/g, "_$1"); //_ac
"abc".replace(/([a-z])([a-z])/g, "_$1*$2"); //_a*bc

//(?:exp)
"abc".replace(/(?:[a-z])([a-z])/g, "_$1"); //_bc

//(?<name>exp)
"abc".replace(/(?<test>a)/g, "_$<test>"); //_abc
"abc".replace(/(?<test>a)/g, "_$1"); //_abc

//分组影响
"abbbc".replace(/ab{1,2}/g, "_c"); //_cbc
"abbbc".replace(/(?:ab){1,2}/g, "_c"); //_cbbc
"abbbc".replace(/(ab){1,2}/g, "_c"); //_cbbc

//断言
"1abc".replace(/1(?=a)/g, "_x"); //_xabc
"1abc".replace(/1(?=c)/g, "_x"); //1abc

"1abc".replace(/1(?!c)/g, "_x"); //_xabc
"1abc".replace(/1(?!a)/g, "_x"); //1abc

"1abc".replace(/(?<=a)b/g, "_x"); //1a_xc
"1abc".replace(/(?<=1)b/g, "_x"); //1abc

"1abc".replace(/(?<!1)b/g, "_x"); //1a_xc
"1abc".replace(/(?<!a)b/g, "_x"); //1abc

额外说明

(?:exp) 使用方式其实和 (exp) 是一样的, 只是一个捕获分组 一个不捕获. 通常只会和 (exp) 一起使用, 调整占位符的索引位置

exp1(?=exp2)exp1(?!exp2) 作用和字面意思一样, 匹配符合规则的字符串. 但注意的是 它们最后都只会匹配 exp1 内容, 和 exp2 无关, exp2 只作为一个规则使用, 不会参与到匹配的结果当中

捕获分组时, 系统会自动给每个分组按照匹配顺序依次取名 就是 $1,$2..., 哪怕你 使用了自定义取名 $<name>, $1,$2... 也是可以使用的

如果你错过了 分组替换, 现在不妨去看看, 你会有新的收获

修饰符

字符对应说明
iignoreCase 的缩写,表示忽略字母的大小写
mmultiline 的缩写,更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n 之前的位置以及字符串结束前的位置.)
gglobal 的缩写,进行全局匹配,即对字符串进行全文匹配,直到字符串遍历结束

例子

"abc".replace(/[A-Z]{2}/i, "x"); //xc

"ab\nabc\nabcd".replace(/^a/m, "x"); //xb\nabc\nabcd

"ab\nabc\nabcd".replace(/^a/gm, "x"); //xb\nxbc\nxbcd

"ab\nabc\nabcd".replace(/c$/gm, "x"); //ab\nabx\nabcd

// \n 的另一种表达形式
`ab
abc
abcd`.replace(/^a/gm, "x"); //xb\nxbc\nxbcd

"ab\nabc".replace(/^A/gim, "x"); //xb\nxbc

修饰符 可以说是 最常用的 字符了, 简单好用, 细心的朋友应该也看到了, 不同的 修饰符 是可以同时使用的, 且不受顺序影响 mg, gm, img, gim 都可以

结语

正则表达式到这里就结束了, 剩下的就是自由发挥, 根据情况来组合不同规则实现复杂的字符串处理, 像现在的 vscode idea webStore … 等等工具, 都有 正则替换内容的功能, 能掌握正则表达式 会使开发更加便利,轻松

如果忘记什么, 或者想推荐别人使用正则表达式都可以查看本文档, 我会查缺补漏,尽量使例子覆盖完整

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值