最详细 so easy 的正则表达式(一)

发布于:2022-11-01 ⋅ 阅读:(272) ⋅ 点赞:(0)

正则表达式

sorry!朋友们,有点标题党。
但是我希望通过这篇文章,你能够对正则表达式有着全新的认识和感想!能够熟练运用正则表达式!

RegExp

ECMAScript 通过 RegExp 类型支持正则表达式。

正则表达式使用类似的 Perl 的简洁语法来创建。

不懂 Perl 没关系,我们看看例子就能很好的理解。

let expression = /pattern/flags;
/* pattern 模式 
	可以是任何简单或复杂的正则表达式
*/
/* flags 标记
	用于控制正则表达式的行为
*/

下面为常用的表示匹配模式的标记:

· g : 全局模式 ,词如其面,表示查找字符串的全部内容,并不是找到第一个匹配内容就停止。

· i :不区分大小写模式,表示在查找匹配时忽略 pattern 和字符串的大小写。

· m:多行模式,表示查找到第一行文本末尾时会继续查找。

还有 y u s有兴趣的小伙伴可以查查,这些标记用的频率比较少。


javascirpt 中的正则表达式用 RegExp 对象来表示,有两种写法:①字面量写法;②构造函数的写法;

var RegExp = /abc/;	//字面量创建
// 上述简单理解就是只要包含有abc这个字符串返回的都是true
// var RegExp = new RegExp(/abc/); //利用RegExp对象来创建
// /abc/g	匹配字符串中所有的"abc"
// /abc/i	匹配第一个"abc",忽略大小写
// /abc/gi	思考题?

//	test() 方法返回一个布尔值表示是否可以找到匹配项
console.log(RegExp.test('abc'));//true
console.log(RegExp.test('a'));//false
console.log(RegExp.test(123));//false

元字符

一定要认真看,非常重要!!!

看看有些什么?【javascript中共有14个元字符】

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

元字符在正则表达式都有一种或多种特殊功能,在网上搜索其用法和行为描述,大家都有可能认为太多了,记不住,那么我们一个一个地理解与分析,通过一定量的实例帮助我们解决难疑。

tips:看完之后一定自己亲手上机动手实践。毕竟实践是认识的基础

元字符 匹配对象
^ 行的起始位置
$ 行的结束位置

例1

var reg = /^abc/;//表示匹配以abc开头
console.log(reg.test('abc'));//true
console.log(reg.test('abcd'));//true
console.log(reg.test('aabcd'));//false

//--------------水平分割线------------------------

var reg = /abc$/;//表示匹配以abc结尾
console.log(reg.test('abc'));//true
console.log(reg.test('123abc'));//true
console.log(reg.test('abcc'));//false

//--------------水平分割线------------------------

var reg = /^abc$/;//精确匹配 要求必须是"abc"字符串才符合规范
console.log(reg.test('abc'));//true
console.log(reg.test('abcd'));//false
console.log(reg.test('aabcd'));//false

哈哈,小伙伴们觉得简单吧,其实正则表达式也不是很难!

难度升级 ↓

元字符 匹配对象
[ ] 列出的个任意字符
{ } 行的结束位置
***** 匹配0次或
+ 匹配1次或
匹配0次或1
( ) 限制多选结构范围,标注量词作用的元素,为反向引用捕获文本
[ ^ ] 列出的单个字符
. 单个任意字符(除回车**\r**、换行**\n** …)
| 分割两边的任意一个表达式

例2

var rg = /[abc]/;
//字符类: [] 表示有一系列字符可供选择,只要匹配其中一个就可以了
//也就是说 只要包含有 a 或者包含有 b 或者包含有 c 都返回为true
console.log(rg.test('Davis'));//true
console.log(rg.test('James'));//true
console.log(rg.test('color'));//true
console.log(rg.test('red'));//false

//--------------水平分割线------------------------
// 温故知新 $ ^
var rg1 = /^[abc]$/;
// 简单理解
// 三选一 只有是 a 或者是 b 或者是 c 这三个字母才返回 true
console.log(rg1.test('aa'));//false
console.log(rg1.test('a'));//true
console.log(rg1.test('b'));//true
console.log(rg1.test('c'));//true
console.log(rg1.test('abc'));//false

字符 - 提供了范围表示法,可以简化字符组

// /[0123456789]/
// 等价于/[0-9]/
// /[abcdefghijklmnopqrstuvwxyz]/
// 等价于/[a-z]/

例3

// [-]方括号内部范围符
var reg = /^[a-z]$/;
// 26个英文字母任何一个字母返回 true  - 表示的是 a 到 z 的范围
console.log(reg.test('a'));//true
console.log(reg.test('z'));//true
console.log(reg.test(1));//false
console.log(reg.test('A'));//false 思考如果变为true 什么方法

//--------------水平分割线------------------------

// 玩点多样 变式
// 字符组合
// var reg1 = /^[a-zA-Z]$/;
// 26个英文字母(大写和小写都可以)任何一个字母返回 true
// var reg2 = /^[a-zA-Z0-9]$/;
// 26个英文字母(大写和小写都可以) + 数字 0~9 任何返回 true
var reg1 = /^[a-zA-Z0-9_-]$/;
console.log(reg1.test('a'));//true
console.log(reg1.test('B'));//true
console.log(reg1.test('8'));//true
console.log(reg1.test('_'));//true
console.log(reg1.test('-'));//true
console.log(reg1.test('!'));//false

//--------------水平分割线------------------------

var reg2 = /^[^a-zA-Z0-9_-]$/;
//内部取反
//!!!!如果中括号里面有^ 表示【取反】的意思 千万不要和我们边界符 ^ 别混淆!!!!
console.log(reg2.test('a'));//false
console.log(reg2.test('B'));//false
console.log(reg2.test('8'));//false
console.log(reg2.test('_'));//false
console.log(reg2.test('-'));//false
console.log(reg2.test('!'));//true

例4

量词的使用

// 量词符: 用来设定某个模式出现的次数
// ^ $ + ? {} 等等
// 简单理解: 就是让下面的a这个字符重复多少次
var reg0 = /^a$/; //重复1次
console.log(reg0.test(''));//false
console.log(reg0.test('a'));//true
console.log(reg0.test('aaaa'));//false

//--------------水平分割线------------------------

//  * 相当于 >= 0 可以出现0次或者很多次 
var reg = /^a*$/;
console.log(reg.test(''));//true
console.log(reg.test('a'));//true
console.log(reg.test('aaaa'));//true

//--------------水平分割线------------------------

//  + 相当于 >= 1 可以出现1次或者很多次
var reg1 = /^a+$/;
console.log(reg1.test(''));//false
console.log(reg1.test('a'));//true
console.log(reg1.test('aaaa'));//true

//--------------水平分割线------------------------

//  ?  相当于 1 || 0
var reg2 = /^a?$/;
console.log(reg2.test(''));//true
console.log(reg2.test('a'));//true
console.log(reg2.test('aaaa'));//false

//--------------水平分割线------------------------

//  { n } 就是重复 n 次
var reg3 = /^a{3}$/; //{ 3 } 就是重复 3 次
console.log(reg3.test(''));//false
console.log(reg3.test('a'));//false
console.log(reg3.test('aaaa'));//false
console.log(reg3.test('aaa'));//true

//--------------水平分割线------------------------

//  {n, }  大于等于n
var reg4 = /^a{3,}$/;
console.log(reg4.test(''));//false
console.log(reg4.test('a'));//false
console.log(reg4.test('aaaa'));//true
console.log(reg4.test('aaa'));//true

//--------------水平分割线------------------------

//  {n,m}  大于等于 n 并且小于等于 m
var reg5 = /^a{3,6}$/;//a重复次数大于等于 3 并且小于等于 6
console.log(reg5.test(''));//false
console.log(reg5.test('a'));//false
console.log(reg5.test('aaaa'));//true
console.log(reg5.test('aaa'));//true
console.log(reg5.test('aaaaaaaaaaaa'));//false

转义字符

​ 转义字符表示为反斜杠\+字符的形式。

​ 上述讲到,元字符有着特殊的含义,是无法直接匹配的,如果要匹配它们本身,就必须使用反斜杠来转义。

例5

let reg0 = /\[bc\]at/i;//匹配第一个"[bc]at",忽略大小写
console.log(reg0.test('[bc]at'));//true
console.log(reg0.test('bcat'));//false

//--------------水平分割线------------------------

let reg1 = /\.at/gi;//匹配所有".at",忽略大小写
console.log(reg1.test('.at'));//true
console.log(reg1.test('I Love Jisoo.at'));/true

注意:并非是14个元字符都需要转义,}] 就不需要转义。

字符\ 在正则表达式字符串中通常被转义为 \\,也就是说想打出 \ 应该怎么办?应该使用 \\

预定义类

​ 指的是某些常见模式的简写方式。

预定义类 说明
\d d(digit)数字 等同于[0-9]
\D 非数字 等同于 [^0-9]
\s 匹配空格 相当于[\t\r\n\v\f]
\S 匹配非空格的字符 相当于[^\t\r\n\v\f]
\w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
\W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]

灵活使用预定义类,比如 [\s\S] [\w\W] [\d\D]可以表示任意字符,注意啦,一般大家认为点号(.)可以代表任何字符,其实并不是,其表示除了回车(\r),换行(\n),行分隔符(\u2028)和段分隔符(\u2029)以外的任意字符。

例6

let reg1 = /[\s\S]/;
let reg2 = /./;
console.log(reg1.test('\r'));//true
console.log(reg2.test('\r'));//false

贪婪模式 (greedy quantifier)

​ 默认情况下,量词都是贪婪模式,即匹配到下一个字符直到不满足匹配规则为止。

例7

let reg = /a*/;
//我们知道星号(*)代表匹配0到多次
//注意:匹配0次 ==> 空字符串
//exec()方法以数组的形式返回匹配结果
console.log(reg.exec('aaabb'));//['aaa']

懒惰模式

​ 也叫作非贪婪模式,贪婪模式是尽可能最大长度匹配,非贪婪模式就是尽可能最小长度匹配,就因为它懒呗,即满足条件就不往下匹配了,使用方法就是在量词的后面加一个问号(?),比如 a*?

//	温故知新 + 变式
//	{n}?	匹配 n 次
//	{n,m}?	匹配至少 n 次,最多 m 次
//	{n,}?	匹配至少 n 次
//	??		相当于	{0,1}
//	*?		相当于	{0,}
//	+?		相当于	{1,}

//感受一下贪婪模式与懒惰模式的区别
//匹配文本 "Today" is such a "beautiful day"
let reg = /\".+\"/;
/*
共找到 1 处匹配:
"Today" is such a "beautiful day"
*/
let reg1 = /\".+?\"/;
/*
共找到 2 处匹配:
"Today"
"beautiful day"
*/

括号

功能:分组引用

具体来说,用于限定量词或选择项的作用范围,也可以用于捕获文本并进行引用或反向引用。

分组

​ 被括号括起来的部分可以称为子表达式,会被保存成一个子组。

​ 例如,如果希望字符串'ab'重复出现2次,应该写成(ab){2},而不是写成ab{2},这表示b重复2次,所以分组和未分组区别很大。

例8

let reg = /(ab){2}/;
console.log(reg.test('abab'));//true
console.log(reg.test('abb'));//false
let reg1 = /ab{2}/;
console.log(reg1.test('abab'));//false
console.log(reg1.test('abb'));//true

​ 再如,现在有个日期 2022-11-01 09:32:10 ,我们想要提取其中的日期和时间,就要用括号对日期和时间进行分组。

2022-11-01

(\d{4}-\d{2}-\d{2}) 这是第一个分组

09:32:10

(\d{2}:\d{2}:\d{2}) 这是第二个分组

​ 可以知道,日期分组是第1个,时间分组是第2个,我们以左括号的位置,来表示分组的标号,从1开始,第几个左括号,就是第几个分组

​ 如果想要提取其中的年、月、日、时、分、秒,那应该

如何分组?很简单,分别对需要提取的信息分组就好了。

2022-11-01

((\d{4})-(\d{2})-(\d{2}))

年月日的分组编号为 2,3,4

09:32:10

((\d{2})-(\d{2})-(\d{2}))

时 分 秒的分组编号为 6,7,8


不保存子组

​ 分组是有一定的性能消耗的,有些情况下,我们只是单纯的想要分组,后续并不想使用它,就可以在左括号的后面加上 ?: 表示不保存子组。

​ 来个例子,我们知道身份证的长度有15位和18位两种,应该如何表示呢?

功能 示例 说明
保存子组 \d{15}(\d{3})? 默认保存子组,后续可以再引用
不保存子组 \d{15}(?:\d{3})? 后续无法再引用括号里的内容

差不多该讲讲捕获了,在讲之前我们先谈谈 RegExp实例方法

RegExp实例

属性

Name type description
global 布尔值 表示是否设置了g标志
ignoreCase 布尔值 表示是否设置了i标志
multiline 布尔值 表示是否设置了m标志
lastIndex 整数 表示在源字符串中下一次搜索的开始位置,始终从0开始
flags 正则表达式的标记字符串

还有许多属性,这里就不一一介绍了。

例9

let reg = /abc/gi;
console.log(reg.global);//true
console.log(reg.ignoreCase);//true
console.log(reg.multiline);//false
console.log(reg.lastIndex);//0
console.log(reg.flags);//gi

let reg1 = new RegExp("abc","gi");
// you can take a try...
// console.log(...)

捕获

​ 括号不仅可以对元素进行分组,还会保存每个分组匹配的文本,等匹配完成后,引用捕获的内容,因为捕获了文本,所以这种功能叫作捕获分组。

​ 嘿,还记得这个例子嘛?

​ 匹配诸如2022-11-01这样的日期字符串

(\d{4})-(\d{2})-(\d{2})

​ 我们知道这分别代表第1、2、3个捕获组。

​ 在 javascript 中有9个用于存储捕获组的构造函数属性

RegExp.$1 RegExp.$2 RegExp.$3...到 RegExp.$9在调用 exec()方法或者 test()方法时,这些属性会被自动填充。

例10

let reg = /(\d{4})-(\d{2})-(\d{2})/;
console.log(reg.test('2022-11-01'));//不要把之前讲的test方法忘记啦!
console.log(RegExp.$1);//2022
console.log(RegExp.$2);//11
console.log(RegExp.$3);//01

捕获分组的文本数量可以进行数据提取,还可以用于替换

replace()方法闪亮登场!

该方法用于数据替换,接受两个参数,第一个参数为有待查找的内容,第二个参数为替换的内容。

replace()方法中也可以使用引用分组,形式是 $1 $2...,其对应的是分组的编号

例11

// 日期的表示方法有很多
// 2022-11-01 或者 2022.11.01 等等
let txt = "2022-11-01";
let reg = /-/g;
console.log(txt.replace(reg,'.'));//2022.11.01

//--------------水平分割线------------------------

let txt1 = "2022-11-01";
let reg1 = /(\d{4})-(\d{2})-(\d{2})/g;
//顺便复习一下分组编号
console.log(txt1.replace(reg1,'$3-$2-$1'));//01-11-2022

方法

RegExp的主要方法是 exec(),主要用于配合捕获组使用。这个方法只接受一个参数,即要应用模式的字符串。

​ 如果找到了匹配项,则返回包含第一个匹配信息的数组;否则返回 null。返回的数组虽然是 Array的实例,但是包含两个额外的属性,分别是 indexinput,词如其面,index是字符串中匹配模式的起始位置,input是要查找的字符串。

​ 这个数组的第一个元素是匹配整个模式的字符串,其他元素是与表达式中捕获组匹配的字符串,如果模式中没有捕获组,则数组只包含一个元素。

例12

let txt = "LeBron and Davis and Russell";
let reg = /LeBron( and Davis( and Russell)?)?/gi;
console.log(reg.exec(txt));

在这里插入图片描述

例10中,模式包含两个捕获组,最内部()的匹配项" and Russell",以及外部的匹配项" and Davis"或者是" and Davis and Russell"

注意:如果匹配模式中设置了 g标记,则每次调用 exec()方法都会在字符串中向前搜索下一个匹配项。

例13

let txt = "LeBron and Kyrie and Russell";
let reg = /.e/g;
console.log(reg.exec(txt));
console.log(reg.exec(txt));
console.log(reg.exec(txt));

在这里插入图片描述

lastIndex的值可以打断点,在浏览器的源代码的作用域中脚本里可以查看。

lastIndex 随着运行过程,一直在变化 :

lastIndex :0

lastIndex :2

lastIndex :16

lastIndex :26

也就是说,在全局匹配模式下,每次调用 exec()方法都会更新 lastIndex的值,以反映上次匹配的最后一个字符的索引。


嘿,我来考考你,上代码!

let r = /\{\{(.+?)\}\}/g;
//具体作用是啥呢?
//你能够想到点什么?

看完之后,可以看看这篇文章哦!其中涉及到本文的重要内容
链接地址:https://blog.csdn.net/m0_67594336/article/details/127618199

后面还会继续更新!