正则表达式
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
的实例,但是包含两个额外的属性,分别是 index
和 input
,词如其面,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