天天看点

JS正则创建方式正则表达式由两个部分组成或 |小括号()中括号[]正则捕获正则的贪婪性RegExp.$1-$9正则不包含数字千分符常用的正则表达式

创建方式

创建方式有两种

//字面量方式,两个斜杠包起来一些正则元字符
let reg = /\d+/g;
//构造函数方式,注意斜杠转义
let reg = new RegExp("\\d+", "g");
           

两种创建方式的区别

1、构造函数方式要特别注意反斜杠转义,因为在字符串中 \ 本来就特殊,比如你想输出"\"

let str1 = '\'    //会报错
let str2 = '\\'   //正确
           

2、字面量方式不能插入变量,因为//中间的都是正则元字符,所以要插入变量创建正则的话就只能用构造函数方式

let name = 'sam';
let reg1 = /^--+name+--$/;
let reg2 = new RegExp("^--"+name+"--$");
reg1.test('--sam--')    //false
reg2.test('--sam--')    //true
           

正则表达式由两个部分组成

元字符

常用量词元字符

*  零到多次
+  1到多次
?  0次或1次
{n}  出现n次
{n,}  出现n到多次
{n,m}  出现n到m次
           

特殊元字符

\  转义字符
.  除\n(换行符)以外的任意字符
^  以哪个元字符开始
$  以哪个元字符结束
\n  换行符
\b  单词边界
\d  0-9之间的一个数字
\D  非\d
\w  数字、字母、下划线中任意一个字符
\W  非\w
\s  一个空白字符(包括空格、制表符、换页符等)
\t  一个制表符(Tab键:4个空格)
x|y  x或y中的一个字符
[xyz]  x或y或z中的一个字符
[^xy]  除了x和y以外的任意字符
[a-z]  指定a-z范围内的任意字符  //[0-9a-zA-Z_]===\w
[^a-z]  非上一个
()  正则中的分组
(?:)  只匹配不捕获
(?=)  正向预查
(?!)  反向预查
           

修饰符

i  ignoreCase 忽略大小写
m  multiline 可以多行匹配
g  global 全局匹配
           

或 |

| 直接使用的时候可能会出现一些不确定性,比如:

let reg = /^18|29$/;
reg.test(18)  //true
reg.test(29)  //true
reg.test(129)  //true
reg.test(189)  //true
reg.test(229)  //true
reg.test(1236629)  //true
//这时reg匹配的是/(^18)|(29$)/以18开始或者是29结束
//如果你想匹配的是18或者29,那么
reg = /^(18|29)$/;
           

所以,避免不确定性,建议在使用 | 时加上小括号。

小括号()

分组,提高优先级,配合 | 使用

参照上文

分组引用

通过"\数字"让该位置和对应分组(根据数字指定第几个分组)的内容相同。

//比如我想匹配像book这样第二和第三个字母一样的单词
var reg = /^[a-zA-Z]{4}$/;    //这样达是不到要求
reg.test('book');    //true
reg.test('boak');    //true

reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/;    //\1位置内容与第一个分组内容相同
reg.test('book');    //true
reg.test('boak');    //false

reg = /^([a-zA-Z])[a-zA-Z]\1[a-zA-Z]$/;    //\1位置内容与第一个分组内容相同
reg.test('book');    //false
reg.test('boak');    //false
reg.test('bobk');    //true
           

中括号[]

[]里面没有多位数

[]里面字符大多为原意

正则捕获

exec()

这是RegExp的方法。

  1. 返回null或一个数组。
  2. 每次只捕获一个匹配项。
  3. 如果正则表达式带修饰符g的话,则每执行完一次就会修改正则实例的lastIndex属性,从而实现捕获多个匹配项。
//这是一个国内身份证号码的正则,之所以加这么多小括号是为了便于分组捕获一些关键信息
let reg = /^([1-8]\d{5})([1-2]\d{3})([0-1]\d)([0-3]\d)\d{2}(\d)(\d|X)$/;
reg.exec('431101199912311193');
//捕获结果["431101199912311193", "431101", "1999", "12", "31", "9", "3", index: 0, input: "431101199912311193", groups: undefined]
//对应解释[捕获到的内容, 分组1内容, 分组2内容, 分组3内容, 分组4内容, 分组5内容, 分组6内容, index:开始匹配内容时的索引, input:原文本 ...]
           

注意:如果正则表达式不带修饰符g的话,那么exec()之后reg.lastIndex还是0,如果带g的话,test()和exec()都会改变lastIndex。

match()

这是String的方法。

  1. 返回null或一个数组。
  2. 如果正则表达式不带修饰符g的话,match()和exec()返回值一样,效果一样。
//如果不带g
let reg = /\d+/;
reg.exec('2099年12月31日');//["2099", index: 0, input: "2099年12月31日", groups: undefined]
'2099年12月31日'.match(reg);//["2099", index: 0, input: "2099年12月31日", groups: undefined]
//如果带g
let reg = /\d+/g;
reg.exec('2099年12月31日');//["2099", index: 0, input: "2099年12月31日", groups: undefined]
'2099年12月31日'.match(reg);//["2099", "12", "31"]
           

replace()

这是String的方法。

  1. 返回一个字符串(不改变原字符串)。
  2. 这个函数更多的是配合正则使用。
  3. 如果第二个参数是函数,那么如果正则不匹配的话,该函数不会执行。

案例:时间字符串处理

//时间字符串处理
let time = '2099-12-31';
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
time.replace(reg, '$1年$2月$3日');    //2099年12月31日
           

案例:单词首字母大写

当replace第二个参数是函数时,正则匹配几次该函数就会执行几次,每次的函数实参跟exec()捕获结果一致([匹配的字符串,分组1,分组2...,原文本])。

//单词首字母大写
let str = 'good good study, day day up!';
let reg = /\b([a-zA-z])([a-zA-z]*)\b/g;
str.replace(reg, (...args) => {
    let [, $1, $2] = args;
    return $1.toUpperCase() + $2;
})    //"Good Good Study, Day Day Up!"
           

正则的贪婪性

默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来捕获的,称为正则捕获的贪婪性。

在量词元字符后面加上 ? 即可取消正则捕获贪婪性(按照正则匹配的最短结果捕获)。

let str = '2099年12月31日';
//贪婪匹配
let reg = /\d+/g;    
str.match(reg);    //["2099", "12", "31"]
//懒惰匹配
reg = /\d+?/g;    
str.match(reg);    //["2", "0", "9", "9", "1", "2", "3", "1"]
           

RegExp.$1-$9

RegExp.$1,RegExp.$2...,RegExp.$9分别表示当前正则实例匹配捕获到的分组捕获内容。$1表示正则表达式里的第一个分组,如此类推,这里最多9个。

let str = '2099年12月31日';
let reg = /\d+/g;
reg.test(str);
console.log(RegExp.$1)    //""
reg = /(\d+)/g;    //正则表达式里必须要有分组才有效果
reg.test(str);
console.log(RegExp.$1)    //2099
reg.test(str);    //前面说到test()也是会改变lastIndex的
console.log(RegExp.$1)    //12
reg.test(str);
console.log(RegExp.$1)    //31
           

正则不包含

^((?!不想包含的字符串).)*$
           

数字千分符

普通方法

let num = '3214567812';
num = num.split('').reverse().join('');  //字符串倒序
//字符串还剩1,2位的时候就不用再循环了,避免出现',321,123'这种情况
for(let i = 2; i < num.length - 1; i += 4){
    let left = num.substring(0, i+1);
    let right = num.substring(i+1);
    num = left + ',' + right;
}
num = num.split('').reverse().join('');  //字符串倒序回来
console.log(num)  //"3,214,567,812"
           

正则方法

  1. 捕获后面到结束位是3为数字或3的倍数位数字(?=(\d{3})+$)的1-3位数字(\d{1,3})
let num = '3214567812';
//?= ?!都是只匹配不捕获
num = num.replace(/\d{1,3}(?=(\d{3})+$)/g, c => c + ',');
console.log(num)  //"3,214,567,812"
           

常用的正则表达式

1、验证是否为有效数字

分析:

  1. 第一位有可能是+、- [+-]?
  2. 如果是一位数呢可以是0,如果是多位数那第一位不能是0 (\d|([1-9]\d+))
  3. 是否有小数点 (\.\d+)?
//验证是否为有效数字
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/
           

2、真实汉字姓名

分析:

  1. 汉字 /^[\u4E00-\u9FA5]$/
  2. 长度 2-10位
  3. 可能是译名,中间有 ·,默认最多出现两次
//验证真实汉字姓名
let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/
           

3、国内身份证

分析:

  1. 第一位数(省份只有1-8开头) [1-8]
  2. 第七位数(年份只有1或2开头) [1-2]
  3. 第十一位数(月份只有0或1开头) [0-1]
  4. 第十三位数(日期只有0-3开头) [0-3]
  5. 最后一位数是数字或大写X
//验证国内身份证号码
let reg = /^([1-8]\d{5})([1-2]\d{3})([0-1]\d)([0-3]\d)\d{2}(\d)(\d|X)$/
//之所以加这么多小括号是为了便于分组捕获一些关键信息
           

4、url网址解析

分析:

  1. 协议(可有可无) ((http|https|ftp):\/\/)?
  2. 域名 \w或-多个,以.连接,最后域名后缀只能是数字或字母 (([\w-]+\.)+[a-z0-9]+)
  3. 请求地址(可有可无)不包含?# ((\/[^/?#]*)+)?
  4. 问号传参(可有可无) (\?[^#]+)?
  5. 哈希(可有可无) (#.+)?
let reg = /^(?:(http|https|ftp):\/\/)?((?:[\w-]+\.)+[a-z0-9]+)((?:\/[^/?#]*)+)?(\?[^#]+)?(#.+)?$/i;
let url = 'https://cn.bing.com/search?q=jszaixian&qs=n&form=QBRE&sp=-1&pq=jszaixian&sc=1-9&sk=&cvid=4A0A125EC6EB47ADA79D8234F64C01F3';
console.log(reg.exec(url))
//协议:https
//域名:cn.bing.com
//请求地址:/search
//问号传参:?q=jszaixian&qs=n&form=QBRE&sp=-1&pq=jszaixian&sc=1-9&sk=&cvid=4A0A125EC6EB47ADA79D8234F64C01F3
//哈希:undefined
           

继续阅读