摘要:正则表达式有两种基本字符类型组成。元字符是在正则表达式中有特殊含义的非字母字符。返回数组是有以下元素组成的第一个元素是与正则表达式相匹配的文本。子表达式就是分组。使用指定的分隔符字符串将一个对象分割成字符串数组。
版权声明
一、正则表达式简介 1、什么是正则表达式</>复制代码
转载请告知并注明来源作者
作者:唐金健
网络昵称:御焱
掘金知乎思否专栏:优雅的前端
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
2、可视化正则表达式工具</>复制代码
简单的说,就是按照某种规则去匹配符合条件的字符串。
Regexper:https://regexper.com/
二、RegExp对象</>复制代码
实例化RegExp的两种方式。
两种方式定义RegExp对象。
1、字面量</>复制代码
let reg = /[a-z]{3}/gmi;
let reg = /[a-z]{3}/g;
let reg = /[a-z]{3}/m;
let reg = /[a-z]{3}/i;
标志
g global 代表全局搜索。如果不添加,搜索到第一个匹配停止。
m Multi-Line 代表多行搜索。
i ignore case 代表大小写不敏感,默认大小写敏感。
2、构造函数</>复制代码
let reg = new RegExp("is", "g");
因为JavaScript字符串中属于特殊字符,需要转义。
三、元字符</>复制代码
把元字符当作转义字符。
正则表达式有两种基本字符类型组成。
原义文本字符
元字符
1、原义文本字符表示原本意义上是什么字符,就是什么字符。
2、元字符是在正则表达式中有特殊含义的非字母字符。
* + ? $ ^ . | ( ) { } [ ]
字符 | 含义 |
---|---|
水平制表符 | |
v | 垂直制表符 |
换行符 | |
回车符 | |
空字符 | |
f | 换页符 |
cX | 控制字符,与X对应的控制字符(Ctrl + X) |
类似于转义字符。
四、字符类</>复制代码
表示符合某种特性的字符类别。
使用元字符[]可以构建一个简单的类。
所谓类是指符合某些特性的对象,一个泛指,而不是某个字符。
表达式[abc]把字符a或b或c归为一类,表达式可以匹配这一类中的任意一个字符。
</>复制代码
// replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
"a1b2c3d4e5".replace(/[abc]/g, "0"); //010203d4e5
字符类取反
我们想要替换不是abc中任意一个字符的字符。
</>复制代码
// 元字符 ^ 创建一个 反向类/负向类
"abcdefg".replace(/[^abc]/g, "0"); //abc0000
五、范围类
</>复制代码
匹配这一个范围内的字符。
如果我们想要匹配数字0-9,那么我们可能会这样写[0123456789]。
如果我们想要匹配26个字母,那么我们可能会这样写[abcdefghijklmnopqrstuvwxyz]。
这样略显麻烦,所以才会有范围类。
</>复制代码
// 替换所有数字
"a1c2d3e4f5".replace(/[0-9]/g, "x"); //axcxdxexfx
// 替换所有小写字母
"a1c2d3e4f5".replace(/[a-z]/g, "x"); //x1x2x3x4x5
// []组成的类内部是可以连写的。替换所有大小写字母
"a1C2d3E4f5G6".replace(/[a-zA-Z]/g, "*"); //*1*2*3*4*5*6
疑问
如果我想替换数字,并且连带-符号也一起替换呢?
</>复制代码
// 替换所有数字和横杠
"2018-5-21".replace(/[0-9-]/g, "*"); //*********
六、预定义类
</>复制代码
一些已经定义的类,可以直接使用。
字符 | 等价类 | 含义 |
---|---|---|
. | [^ ] | 除了回车、换行之外的所有字符 |
d | [0-9] | 数字字符 |
D | [^0-9] | 非数字字符 |
s | [ x0B ] | 空白符 |
S | [^ x0B ] | 非空白符 |
w | [a-zA-Z_0-9] | 单词字符(字母、数字、下划线) |
W | [^a-zA-Z_0-9] | 非单词字符 |
替换一个 ab + 数字 + 任意字符 的字符串
</>复制代码
// 写法1
"ab0c".replace(/ab[0-9][^
]/g, "TangJinJian"); //TangJianJian
// 写法2
"ab0c".replace(/abd./g, "TangJinJian"); //TangJianJian
七、单词边界
字符 | 含义 |
---|---|
^ | 以xxx开始(不在中括号内时的含义) |
$ | 以xxx结束 |
单词边界 | |
B | 非单词边界 |
我想替换的字符串,属于那种只在开头出现的。
</>复制代码
"YuYan is a boy, YuYan".replace(/^YuYan/g, "TangJinJian"); //TangJinJian is a boy, YuYan
我想替换的字符串,属于那种只在结尾出现的。
</>复制代码
"YuYan is a boy, YuYan".replace(/YuYan$/g, "TangJinJian"); //YuYan is a boy, TangJinJian
单词边界例子。
</>复制代码
// 替换所有is为0
"This is a man".replace(/is/g, "0"); //Th0 0 a man
// 替换所有is前面带有单词边界的字符串
"This is a man".replace(/is/g, "0"); //This 0 a man
// 替换所有is前面没有单词边界的字符串
"This is a man".replace(/Bis/g, "0"); //Th0 is a man
八、量词
</>复制代码
用来处理连续出现的字符串。
字符 | 含义 |
---|---|
? | 出现零次或一次(最多出现一次) |
+ | 出现一次或多次(至少出现一次) |
* | 出现零次或多次(任意次) |
{n} | 出现n次 |
{n,m} | 出现n到m次 |
{n,} | 至少出现n次 |
我想替换字符串中连续出现10次的数字为*。
</>复制代码
"1234567890abcd".replace(/d{10}/, "*"); //*abcd
我想替换字符串中的QQ号码。
</>复制代码
"我的QQ是:10000".replace(/[1-9][0-9]{4,}/, "19216811"); //我的QQ是:19216811
九、贪婪模式
</>复制代码
尽可能多的匹配。
有这样的一种场景下的正则表达式,/d{3,6}/该替换3个数字还是6个数字呢,4、5个数字?
</>复制代码
// 贪婪模式会尽可能的往多的方面去匹配
"123456789".replace(/d{3,6}/, "x"); //x789
"123456789".replace(/d+/, "x"); //x
"123456789".replace(/d{3,}/, "x"); //x
十、非贪婪模式
</>复制代码
尽可能少的匹配。
如果我们想要最低限度的替换呢?
</>复制代码
// 非贪婪模式使用 ? 尽可能的往少的方面去匹配
"12345678".replace(/d{3,6}?/g, "x"); //xx78
"123456789".replace(/d{3,6}?/g, "x"); //xxx
因为有g标志,会匹配这段字符串里所有符合规则的字符串。
第一个规则/d{3,6}?/g,12345678中有两个符合条件的字符串,是123和456。所以替换结果是xx78。
第二个规则/d{3,6}?/g,123456789中有三个符合条件的字符串,是123、456和789。所以替换结果是xxx。
</>复制代码
括号里的一些规则,分为一组。
我想替换连续出现3次的字母和数字。
</>复制代码
//没有分组的情况下,后面的量词,只是表示匹配3次数字。
"a1b2d3c4".replace(/[a-z]d{3}/g, "*"); //a1b2d3c4
//有分组的情况下,分组后面的量词,表示符合这个分组里规则的字符串,匹配3次。
"a1b2d3c4".replace(/([a-z]d){3}/g, "*"); //*c4
1、或
分组里有两种规则,只要满足其中一种即可匹配。
</>复制代码
//我想把ijaxxy和ijcdxy都替换成*
"ijabxyijcdxy".replace(/ij(ab|cd)xy/g, "*"); //**
2、反向引用
可以把分组视为变量,来引用。
</>复制代码
//我想把改变年月日之间的分隔符
"2018-5-22".replace(/(d{4})-(d{1,2})-(d{1,2})/g, "$1/$2/$3"); //2018/5/22
//我想替换日期,并且更改顺序
"2018-5-22".replace(/(d{4})-(d{1,2})-(d{1,2})/g, "$2/$3/$1"); //5/22/2018
3、忽略分组
忽略掉分组,不捕获分组,只需要在分组内加上?:
</>复制代码
// 忽略掉匹配年的分组后,匹配月的分组变成了$1,日的分组变成了$2
"2018-5-22".replace(/(?:d{4})-(d{1,2})-(d{1,2})/g, "$1/$2/$3"); //5/22/$3
十二、前瞻
</>复制代码
正则表达式从文本头部向尾部开始解析,文本尾部方向,称为“前”。
前瞻就是在正在表达式匹配到规则的时候,向前检查是否符合断言,后顾/后瞻方向相反。
JavaScript不支持后顾。
符合和不符合特定断言称为肯定/正向匹配和否定/负向匹配。
名称 | 正则 | 含义 |
---|---|---|
正向前瞻 | exp(?=assert) | |
负向前瞻 | exp(?!assert) | |
正向后顾 | exp(?<=assert) | JavaScript不支持 |
负向后顾 | exp(? | JavaScript不支持 |
有这样一个单词字符+数字格式的字符串,只要满足这种格式,就把其中的单词字符替换掉。
</>复制代码
"a1b2ccdde3".replace(/w(?=d)/g, "*"); //*1*2ccdd*3
有这样一个单词字符+非数字格式的字符串,只要满足这种格式,就把前面的单词字符替换掉。
</>复制代码
"a1b2ccdde3".replace(/w(?!d)/g, "*"); //a*b*****e*
十三、RegExp对象属性
global是否全文搜索,默认false。
ignore case是否大小写敏感,默认是false。
multiline多行搜索,默认值是false。
lastIndex是当前表达式匹配内容的最后一个字符的下一个位置。
source正则表达式的文本字符串。
</>复制代码
let reg1 = /w/;
let reg2 = /w/gim;
reg1.global; //false
reg1.ignoreCase; //false
reg1.multiline; //false
reg2.global; //true
reg2.ignoreCase; //true
reg2.multiline; //true
十四、RegExp对象方法
1、RegExp.prototype.test()
</>复制代码
用来查看正则表达式与指定的字符串是否匹配。返回true或false。
</>复制代码
let reg1 = /w/;
reg1.test("a"); //true
reg1.test("*"); //false
加上g标志之后,会有些区别。
</>复制代码
let reg1 = /w/g;
// 第一遍
reg1.test("ab"); //true
// 第二遍
reg1.test("ab"); //true
// 第三遍
reg1.test("ab"); //false
// 第四遍
reg1.test("ab"); //true
// 第五遍
reg1.test("ab"); //true
// 第六遍
reg1.test("ab"); //false
实际上这是因为RegExp.lastIndex。每次匹配到之后,lasgIndex会改变。
lastIndex是正则表达式的一个可读可写的整型属性,用来指定下一次匹配的起始索引。
</>复制代码
let reg = /w/g;
// 每次匹配到,就会把lastIndex指向匹配到的字符串后一个字符的索引。
while(reg.test("ab")) {
console.log(reg.lastIndex);
}
// 1
// 2
reg.lastIndex初始时为0,第一个次匹配到a的时候,reg.lastIndex为1。第二次匹配到b的时候,reg.lastIndex为2。
</>复制代码
let reg = /ww/g;
while(reg.test("ab12cd")) {
console.log(reg.lastIndex);
}
// 2
// 4
// 6
reg.lastIndex初始时为0,第一个次匹配到ab的时候,reg.lastIndex为2。第二次匹配到12的时候,reg.lastIndex为4。第三次匹配到cd的时候,reg.lastIndex为6。
</>复制代码
let reg = /w/g;
// 匹配不到符合正则的字符串之后,lastIndex会变为0。
while(reg.test("ab")) {
console.log(reg.lastIndex);
}
console.log(reg.lastIndex);
reg.test("ab");
console.log(reg.lastIndex);
// 1
// 2
// 0
// 1
所以,这就是为什么reg.test("ab")再多次执行之后,返回值为false的原因了。
</>复制代码
let reg = /w/g;
reg.lastIndex = 2;
reg.test("ab"); //false
每次匹配的起始位置,是以lastIndex为起始位置的。上述例子,一开始从位置2开始匹配,位置2后面没有符合正则的字符串,所以为false。
2、RegExp.prototype.exec()</>复制代码
在一个指定字符串中执行一个搜索匹配。返回一个搜索的结果数组或null。
非全局情况
</>复制代码
let reg = /d(w)d/;
let ts = "*1a2b3c";
let ret = reg.exec(ts); //ret是结果数组
// reg.lastIndex肯定是0,因为没有g标志。 没有g标志的情况下,lastIndex被忽略。
console.log(reg.lastIndex + "
" + ret.index + "
" + ret.toString());
console.log(ret);
// 0 1 1a2,a
// ["1a2", "a"]
返回数组是有以下元素组成的:
第一个元素是与正则表达式相匹配的文本。
第二个元素是reg对象的第一个子表达式相匹配的文本(如果有的话)。
第二个元素是reg对象的第二个子表达式相匹配的文本(如果有的话),以此类推。
</>复制代码
// 子表达式就是分组。
let reg = /d(w)(w)(w)d/;
let ts = "*1a2b3c";
let ret = reg.exec(ts);
console.log(reg.lastIndex + "
" + ret.index + "
" + ret.toString());
console.log(ret); //输出结果数组
// 0 1 1a2b3,a,2,b
// ["1a2b3", "a", "2", "b"]
全局情况
</>复制代码
let reg = /d(w)(w)(w)d/g;
let ts = "*1abc25def3g";
while(ret = reg.exec(ts)) {
console.log(reg.lastIndex + "
" + ret.index + "
" + ret.toString());
}
// 6 1 1abc2,a,b,c
// 11 6 5def3,d,e,f
第一次匹配的是1abc2,1abc2的后一个字符的起始位置是6,所以reg.lastIndex是6。
1abc2的第一个字符的起始位置是1,所以ret.index是1。
第二次匹配的是5def3,5def3的后一个字符的起始位置是11,所以reg.lastIndex是11。
5def3的第一个字符的起始位置是6,所以ret.index是6。
</>复制代码
执行正则表达式和String对象之间的一个搜索匹配。
方法返回第一个匹配项的index,搜索不到返回-1。
不执行全局匹配,忽略g标志,并且总是从字符串的开始进行检索。
我想知道Jin字符串的起始位置在哪里。
</>复制代码
"TangJinJian".search("Jin"); //4
"TangJinJian".search(/Jin/); //4
search方法,既可以通过字符串,也可以通过正则描述字符串来搜索匹配。
2、String.prototype.match()</>复制代码
当一个字符串与一个正则表达式匹配时, match()方法检索匹配项。
提供RegExp对象参数是否具有g标志,对结果影响很大。
非全局调用的情况
如果RegExp没有g标志,那么match只能在字符串中,执行一次匹配。
如果没有找到任何匹配文本,将返回null。
否则将返回一个数组,其中存放了与它找到的匹配文本有关的信息。
</>复制代码
let reg = /d(w)d/;
let ts = "*1a2b3c";
let ret = ts.match(reg);
console.log(ret.index + "
" + reg.lastIndex);
console.log(ret);
// 1 0
// ["1a2", "a"]
非全局情况下和RegExp.prototype.exec()方法的效果是一样的。
全局调用的情况我想找到所有数字+单词+数字格式的字符串。
</>复制代码
let reg = /d(w)d/g;
let ts = "*1a2b3c4e";
let ret = ts.match(reg);
console.log(ret.index + "
" + reg.lastIndex);
console.log(ret);
// undefined 0
// ["1a2", "3c4"]
全局情况下和RegExp.prototype.exec()方法的区别。在于,没有了分组信息。
如果我们不使用到分组信息,那么使用String.prototype.match()方法,效率要高一些。而且不需要写循环来逐个所有的匹配项获取。
</>复制代码
使用指定的分隔符字符串将一个String对象分割成字符串数组。
</>复制代码
"a,b,c,d".split(/,/); //["a", "b", "c", "d"]
"a1b2c3d".split(/d/); //["a", "b", "c", "d"]
"a1b-c|d".split(/[d-|]/); //["a", "b", "c", "d"]
4、String.prototype.replace()
常规用法</>复制代码
返回一个由替换值替换一些或所有匹配的模式后的新字符串。模式可以是一个字符串或者一个正则表达式, 替换值可以是一个字符串或者一个每次匹配都要调用的函数。
</>复制代码
"TangJinJian".replace("Tang", ""); //JinJian
"TangJinJian".replace(/Ji/g, "*"); //Tang*n*an
以上两种用法,是最常用的,但是还不能精细化控制。
精细化用法我想要把a1b2c3d4中的数字都加一,变成a2b3c4d5。
</>复制代码
"a1b2c3d4".replace(/d/g, function(match, index, orgin) {
console.log(index);
return parseInt(match) + 1;
});
// 1
// 3
// 5
// 7
// a2b3c4d5
回调函数有以下参数:
match第一个参数。匹配到的字符串。
group第二个参数。分组,如果有n个分组,则以此类推n个group参数,下面两个参数将变为第2+n和3+n个参数。没有分组,则没有该参数。
index第三个参数。匹配到的字符串第一个字符索引位置。
orgin第四个参数。源字符串。
我想把两个数字之间的字母去掉。
</>复制代码
"a1b2c3d4e5f6".replace(/(d)(w)(d)/g, function(match, group1, group2, group3, index, orgin) {
console.log(match);
return group1 + group3;
});
// 1b2
// 3d4
// 5f6
// a12c34e56
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/95191.html
摘要:目前许多程序设计语言都支持利用正则表达式进行字符串操作。本文中的正则表达式转化为关系图来展示的工具是此文主要参考和学习了老姚的正则表达式迷你书,内容清晰明了,在此非常感谢老姚的精神,致敬。参考文献老姚著正则表达式迷你书 sharplook作为专业的日志采集分析系统,涉及的技术点,从后到前着实不少,内容也较为复杂。正则作为日志解析的手段,起着举足轻重的作用,在此小生将晦涩难懂的内容,拆解...
Javascript的正则表达式是前端中比较重要的部分,正则表达式主要用于字符串处理,表单验证等场合,实用高效,文章主要对JavaScript中的正则的学习与总结 正则表达式的定义 正则表达式:是一个描述字符模式的对象,JavaScrip中正则表达式用RegExp对象表示,可以使用RegExp构造函数来创建正则对象 正则表达式的创建 1.字面量创建 var reg = /[a-z]/; 2.构...
阅读 3840·2021-11-24 09:39
阅读 3087·2021-11-16 11:49
阅读 2156·2019-08-30 13:54
阅读 1191·2019-08-30 13:03
阅读 1170·2019-08-30 11:10
阅读 858·2019-08-29 17:10
阅读 1328·2019-08-29 15:04
阅读 1289·2019-08-29 13:02