正则表达式从基础到深入实战
Posted 珠峰培训
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正则表达式从基础到深入实战相关的知识,希望对你有一定的参考价值。
什么是正则?
正则就是一个规则,用来处理
字符串
的规则
1、正则匹配
编写一个规则,验证某个字符串是否符合这个规则,正则匹配使用的是 test 方法2、正则捕获
编写一个规则,在一个字符串中把符合规则的内容都获取到,正则捕获使用的方法:正则的exec方法、字符串中的split、replace、match等方法都支持正则
var reg = /^$/;
//=>两个斜杠中间包含一些内容就是正则,两个斜杠之间包含的全部内容都是元字符
正则的元字符和修饰符
任何一个正则都是由 元字符 和 修饰符 组成的
`修饰符`
g(global):全局匹配
i(ignoreCase):忽略大小写匹配
m(multiline):多行匹配
`元字符`
[量词元字符]
+:让前面的元字符出现一到多次
?:出现零到一次
*:出现零到多次
{n}:出现n次
{n,}:出现n到多次
{n,m}:出现n到m次
[特殊意义的元字符]
\:转义字符(把一个普通字符转变为有特殊意义的字符,或者把一个有意义字符转换为普通的字符)
.:除了\n(换行符)以外的任意字符
\d:匹配一个0~9之间的数字
\D:匹配任意一个非0~9之间的数字(大写字母和小写字母的组合正好是反向的)
\w:匹配一个 `0~9或字母或_` 之间的字符
\s:匹配一个任意空白字符
\b:匹配一个边界符
x|y:匹配x或者y中的一个
[a-z]:匹配a-z中的任意一个字符
[^a-z]:和上面的相反,匹配任意一个非a-z的字符
[xyz]:匹配x或者y或者z中的一个字符
[^xyz]:匹配除了xyz以外的任意字符
():正则的小分组,匹配一个小分组(小分组可以理解为大正则中的一个小正则)
^:以某一个元字符开始
$:以某一个元字符结束
?::只匹配不捕获
?=:正向预查
?!:负向预查
除了以上特殊元字符和量词元字符,其余的都叫做普通元字符:代表本身意义的元字符
元字符详细解读
^ $
var reg = /\d+/;
//=>包含某某某即可,这里说明包含1到多个数字即可
var str = '珠峰2017培训2018';
reg.test(str) =>truereg=/^\d+/;
reg.test(str) =>falsereg=/^\d+$/;
//=>只能是某某某的,这里说明只能是1到多个数字
reg.test('2017'); =>truereg.test('2017珠峰2018'); =>falsereg.test('2'); =>true
^或者$只是一个修饰或者声明,不会占据字符串的位置
\
var reg = /^2.3$/;
reg.test('2.3'); =>truereg.test('2+3'); =>true
点在正则中的意思:匹配除了\n以外的任意字符,而不是单纯的小数点
reg = /^2\.3$/;
reg.test('2.3'); =>truereg.test('2+3'); =>false
使用转义字符把点转换为本身小数点的意思
x|y
var reg = /^18|19$/;
//=>18 19 189 119 819 181 1819 ... 很多都符合这个规则/* * 18或者19 * 以1开头 以9结尾 中间是8或者1 * 以18开头或者以19结尾即可 =>'18珠峰' '珠峰19'... */
var reg = /^(18|19)$/;
//=>此时只有18或者19符合我们的规则了
()
:正则中的分组,也可以理解为一个大正则中的一个正则(包起来的部分是一个整体);在正则中我们可以使用小括号改变一些默认的优先级
;小分组还有第二个作用:
分组引用
小分组的第三个作用:分组捕获
//=>分组引用:\1 或者 \2 ...出现和第N个分组一模一样的内容
var reg = /^([a-z])([a-z])\2([a-z])$/;
//=> 符合的字符串:foot、book、week、attr、http...
[]
[xyz] [^xyz] [a-z] [^a-z]
```javascript
//=>\w:数组字母下划线中的任意一个字符
var reg = /^[a-zA-Z0-9_]$/; //=>等价于\w//=>中括号中出现的元字符,一般都代表本身的含义
var reg = /^[.?+&]+$/; //=>里面的四个元字符都是本身含义,例如:点就是小数点了,不是所谓的任意字符…//=>需求:描述样式类名的规则(数字、字母、下划线、-),并且不能以-开头
//var reg = /^[\w-]+//=>没有处理以-开头的情况
var reg = /^\w[\w-]*$/;
//=>需求:验证18~65之间的年龄
//var reg = /^[18-65]$/;
//=>1或者8~6或者5中的任意一个字符,中括号中出现的18不是数字18,而是1或者8,当前正则是非法的:因为不能设置8~6这种范围//
=>分三个阶段处理:
// 18 或者 19 /(18|19)/
// 20 ~ 59 /([2-5]\d)/
// 60 ~ 65 /(6[0-5])/
var reg = /^((18|19)|([2-5]\d)|(6[0-5]))$/;
常用的正则表达式编写
验证是否为有效数字
/* * 可能是正数,可能是负数 12 -12 * 整数或者小数 0 12 0.2 12.5 -12.3 * 只要出现小数点,后面至少要跟一位数字 * 小数点前面必须有数字 */var reg = /^-?(\d|([1-9]\d+))(\.\d+)?$/;/* * -? 负号可有可无 * (\d|([1-9]\d+)) * \d 一位数可以是任何值 * ([1-9]\d+) 多位数不能以零开头 * (\.\d+)? 小数部分可有可无,有的话点后面必须跟一位数字 */
手机号码
/* * 11位数字 * 1开头 */var reg = /^1\d{10}$/;
用户名:真实姓名
//=>/^[\u4E00-\u9FA5]$/ 中文汉字的正则
var reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10})?$/;
邮箱
var reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;/* * 以数字字母下划线开头 * @前面可以是 数字、字母、下划线、-、. 这些符号 * 不能把 -和. 连续出现,出现一次后面必须跟数字字母下划线 * * @后面的部分支持 * 企业邮箱 * .com.cn 多域名情况 */// [A-Za-z0-9]+// ((\.|-)[A-Za-z0-9]+)*// \.[A-Za-z0-9]+// @163.com.cn// @zhu-feng-pei-xun.com.cn
身份证号码
/* * 18位 * 前17位必须是数字 * 最后一位可以是数字或者X(X代表数字10) * * 130828199012040617 * 前六位:省市县 130828 * 接下来八位 出生年+月+日 * 倒数第二位数字 奇数代表男 偶数代表女 */var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
//=>这样写不仅可以匹配,而且以后捕获的时候,不仅可以把大正则匹配的结果捕获到,里面每一个小分组(小正则)匹配的结果也可以单独的捕获到 “分组捕获”//=>年 1950~2017//=>第一段 1950~1999//=>第二段 2000~2017//==> 00~09//==> 10~17// /^((19[5-9]\d)|(20((0\d)|(1[0-7]))))$/
正则捕获
把当前字符串中符合正则的字符捕获到
RegExp.prototype:exec
实现正则捕获的方法
var str = '珠峰培训2017扬帆起航2018';
var reg = /\d+/;
reg.exec(str);/* * 当正则捕获的时候: * 1、先去验证当前字符串和正则是否匹配,如果不匹配返回的结果是null(没有捕获到任何的内容) * 2、如果匹配,从字符串最左边开始,向右查找到匹配的内容,并且把匹配的内容返回 * * exec捕获到结果的格式: * -> 获取的结果是一个数组 * -> 数组中的第一项是当前本次大正则在字符串中匹配到的结果 * -> index:记录了当前本次捕获到结果的起始索引 * -> input:当前正则操作的原始字符串 * -> 如果当前正则中有分组,获取的数组中,从第二项开始都是每个小分组,本次匹配到的结果(通过exec可以把分组中的内容捕获到) * * 执行一次exec只能把符合正则规则条件中的一个内容捕获都,如果还有其它符合规则的,需要在次执行exec才有可能捕获到 */
正则捕获存在懒惰性
执行一次exec捕获到第一个符合规则的内容,第二次执行exec,捕获到的依然是第一个匹配的内容,后面匹配的内容不管执行多少次exec都无法捕获到
解决正则捕获的懒惰性:
在正则的末尾加修饰符g(全局匹配)
//=>正则为什么会存在懒惰性?/* * 正则本身有一个属性:lastIndex(下一次正则在字符串中匹配查找的开始索引) * 默认值:0,从字符串第一个字符开始查找匹配的内容 * 默认不管指定多少遍exec方法,正则的lastIndex值都不会变(也就是第二次以后查找的时候还是从第一个字符找,所以找到的结果永远都是第一个匹配的内容) * 而且当我们手动把 lastIndex 进行修改的时候,不会起到任何的作用 *///=>为什么加修饰符g就解决了懒惰性?/* * 加了修饰符g,每一次exec结束后,浏览器默认会把lastIndex值进行修改,下一次从上一次结束的位置开始查找,所以可以得到后面匹配的内容了 */
var reg = /\d+/g;
var str = '珠峰培训2017杨帆起航2018';
console.log(reg.lastIndex);//=>0
console.log(reg.exec(str)[0]);//=>'2017'
console.log(reg.lastIndex);//=>8
console.log(reg.exec(str)[0]);//=>'2018'
console.log(reg.lastIndex);//=>16
console.log(reg.exec(str));//=>null
console.log(reg.lastIndex);//=>0
console.log(reg.exec(str)[0]);//=>'2017'
exec有自己的局限性:执行一次exec只能捕获到一个和正则匹配的结果(即使加了修饰符g),如果需要都捕获到,我们需要执行N次exec方法才可以
下面封装的myExecAll方法,目的是执行一次这个方法,可以把当前正则匹配到的全部内容都捕获到
```javascript
RegExp.prototype.myExecAll = function myExecAll() {
var str = arguments[0] || ‘’,
result = [];
//=>首先判断THIS是否加了全局修饰符G,如果没有加,为了防止下面执行出现死循环,我们只让其执行一次EXEC即可,把执行一次的结果直接的返回
if (!this.global) {
return this.exec(str);
}
var ary = this.exec(str);
while (ary) {
result.push(ary[0]);
ary = this.exec(str);
}
return result;
};var reg = /\d+/g;
console.log(reg.myExecAll(‘珠峰2017培训2018杨帆2019起航2020’));
使用字符串方法match实现捕获
var reg = /\d+/g;
var str = '珠峰2017培训2018杨帆2019起航2020';
str.match(reg); //=>["2017", "2018", "2019", "2020"]
使用字符串match捕获:
1、如果正则加了修饰符g,执行一次match会把所有正则匹配的内容捕获到
2、如果没有加修饰符g,执行一次match只能把第一个匹配的结果捕获到局限性:
在加了修饰符g的情况下,执行match方法只能把大正则匹配的内容捕获到,对于小分组捕获的内容方法给其自动忽略了
```javascript
var str = ‘my name is {0},i am {1} years old~,2017’;
//=>需求:把{n}整体捕获到,而且还要把括号中的数字也获取到
var reg = /{(\d+)}/g;// str.match(reg);//=>[“{0}”, “{1}”]
//=>想要获取小分组中的内容,我们只能使用EXEC处理了
function fn(reg,str){
var ary=reg.exec(str),
result=[];
while(ary){
result.push(ary);
ary=reg.exec(str);
}
return result;
}
使用test也可以实现正则的捕获
不管是正则的匹配还是正则的捕获,在处理时候的原理是没区别的:
从字符串的第一个字符向后查找,找到符合正则规则的字符,如果可以找到,说明正则和字符串匹配(test检测返回true、exec捕获返回捕获的内容),如果找到末尾都没有匹配的,说明正则和字符串不匹配(test检测返回false、exec捕获返回null)
如果正则设置了修饰符g,不管使用test还是exec中的任何方法,都会修改lastIndex值(下一次查找是基于上一次匹配结果的末尾开始查找的)
```javascript
//=>如果当前字符串和正则是匹配的,我们进行捕获
var reg = /{(\d+)}/g;
var str = ‘my name is {0}~~’;
if (reg.test(str)) {
//=>reg.test(str) : true
console.log(reg.lastIndex);//=>14
console.log(reg.exec(str));//=>null
}var reg = /{(\d+)}/;
var str = ‘my name is {0}~~’;
if (reg.test(str)) {
//=>reg.test(str) : true
console.log(reg.lastIndex);//=>0
console.log(reg.exec(str));//=>[‘{0}’,’0’…]
}
使用test不仅可以找到匹配的内容,也能像exec一样把找到的内容获取到
test返回结果是 true/false,所以靠返回结果肯定不行
```javascript
var reg = /{(\d+)}/g;
var str = ‘my name is {0},i am {1} years old‘;
reg.test(str);//=>true
console.log(RegExp.$1);//=>0 获取到当前本次匹配内容中第一个小分组捕获的内容reg.test(str);//=>true
console.log(RegExp.$1);//=>1 TEST可以实现捕获,但是每一次只能获取到当前本次匹配结果中,第N个分组捕获的内容 $1第一个分组 $2第二个分组 …
所有支持正则的方法都可以实现正则的捕获(一般都是字符串方法)
字符串中常用的支持正则的方法:
match
split
replace
…
```javascript
var str = ‘name=珠峰&age=8&lx=teacher’;
str.split(/&|=/); //=>[“name”, “珠峰”, “age”, “8”, “lx”, “teacher”]str.split(/(&|=)/); //=>[“name”, “=”, “珠峰”, “&”, “age”, “=”, “8”, “&”, “lx”, “=”, “teacher”]
//=>在使用split进行字符串拆分的时候,如果正则中包含小分组,会把小分组中的内容都捕获到,放在最后的数组中
//=>本案例中的小括号仅仅是为了实现 改变默认的优先级 问题,但是我们不想把分组中的内容捕获到 => “只想匹配不想捕获” 我们可以使用 (?:)
str.split(/(?:&|=)/); //=>[“name”, “珠峰”, “age”, “8”, “lx”, “teacher”]//=>只匹配不捕获:
//在当前一个分组中加了 ?: ,在正则检测匹配的时候,小分组可以起到自己应有的作用(例如:改变优先级…),但是在捕获的时候,遇到带?:的小分组,浏览器不会把当前这个分组中匹配的内容,单独去捕获了var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|X)$/;
reg.exec(‘130828199012040617’); //=>[“130828199012040617”, “130828”, “1990”, “12”, “04”, “06”, “1”, “7”…]var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(?:\d{2})(\d)(?:\d|X)$/;
reg.exec(‘130828199012040617’);//=> [“130828199012040617”, “130828”, “1990”, “12”, “04”, “1”…]var reg = /^-?(\d|([1-9]\d+))(.\d+)?$/;//=>计算是第几个分组的时候,我们从左向右找 ( 即可)
replace
replace:字符串中原有字符的替换
str.replace(old,new)
```javascript
var str = ‘珠峰2017珠峰2018’;
str = str.replace(‘珠峰’,’珠峰培训’);
str = str.replace(‘珠峰’,’珠峰培训’);
//=>’珠峰培训培训2017珠峰2018’ 没有实现我们希望的效果//=>在不使用正则的情况下,执行一次replace只能替换一个原有字符,第二次执行replace,还是从字符串的开始位置查找,把最新找到的字符替换为新字符(类似于正则捕获时候的懒惰性:每一次执行都是从字符串最开始的位置查找)
真实项目中,replace一般都是和正则搭配在一起使用的
var str = '珠峰2017珠峰2018'; str = str.replace(/珠峰/g,'珠峰培训');//=>"珠峰培训2017珠峰培训2018"
replace原理:
1、当replace方法执行,第一项传递一个正则
正则不加g:把当前字符串中第一个和正则匹配的结果捕获到,替换成新的字符
正则加g:把当前字符串中所有和正则匹配的内容都分别的捕获到,而且每一次捕获,都会把当前捕获的内容替换为新字符2、当replace方法执行,第二个参数传递的是一个函数(回调函数)
首先用正则到字符串中进行查找匹配,匹配到一个符合规则的,就把传递的函数执行一次
不仅执行这个函数,而且还把正则本次捕获的结果(和执行exec捕获的结果一样:数组、大正则匹配、小分组匹配 都有)当做实参传递给这个函数(这样就可以在函数中获取这些值:而这些值就是正则每一次捕获的结果 )
var str = 'my name is {0},i am {1} years old,i can {2}!';
var reg = /\{(\d+)\}/g;
str.replace(reg, function () {
//=>传递的函数一共被执行三次
//=>console.log(arguments) 每一次匹配捕获到结果,不仅把这个方法执行了,而且还会把当前捕获的结果当做实参传递给这个函数(ARG)
/* * 第一次执行函数,获取的是ARG类数组 * 0:'{0}' 本次大正则匹配的结果 * 1:'0' 本次第一个小分组匹配的结果 * 2:11 本次大正则匹配结果在字符串中的索引 index * 3:'my nam...' 原始字符串 input * * 和每一次执行exec实现捕获的结果非常类似 */
//return xxx;
//=>每一次执行函数,函数中RETURN的结果,都相当于把本次大正则匹配的内容替换掉(原始字符串不变)
});
以上是关于正则表达式从基础到深入实战的主要内容,如果未能解决你的问题,请参考以下文章