JS笔记(15): 正则表达式

栏目: JavaScript · 发布时间: 5年前

内容简介:正则(Regular Expression):是一个学习正则其实就是在学习如何编写规则,每一个正则都是由只要在正则中出现的元字符(在基于字面量方式创建),除了特殊和有量词的以外,其余的都是普通元字符

正则(Regular Expression):是一个 处理字符串 的规则

  • 1)正则只能用来处理字符串
  • 2)处理一般包含两方面:
    • A: 正则匹配 :验证当前字符串是否符合某个规则
    • B: 正则捕获 :把一个字符串中符合规则的字符获取到

学习正则其实就是在学习如何编写规则,每一个正则都是由 元字符修饰符 两部分组成

一、创建正则的两种方式

let reg = /^\d+$/g;
let reg2 = new RegExp("^\\d+$", "g");

二、常用的修饰符

常用的修饰符 全称 含义
i ignoreCase 忽略大小写匹配
m multiline 多行匹配
g global 全局匹配
u(ES6) unicode 正确处理四个字节的 UTF-16 编码
y(ES6) sticky 粘连(全局匹配)

三、常用的元字符:

1.特殊元字符

  • \d 0-9之间的一个数字
  • \D 非0-9之间的任意字符
  • \w 数字、字母、下划线中的的任意一个字符 /[0-9a-zA-Z_]/
  • \W 非数字、字母、下划线中的任意字符
  • \s 匹配任意一个空白字符,包括:
    • \t制表符[tab键四个空格]
    • 空格
    • \n换行符
// test() 方法用于检测一个字符串是否匹配某个模式.
let str = '1-7p';
let reg = /\b7p/;
console.log(reg.test(str)); //true
console.log(reg.exec(str)); //[7p...]
复制代码
  • \n 匹配一个换行符 (enter)
let str = 'b7\np';
let reg = /^b7\np$/;
console.log(reg.test(str)); //true
console.log(reg.exec(str)); //["b7↵p"...]
console.log(str.match(reg)); //["b7↵p"...]
复制代码
  • \ 转义字符: 有两个作用
    \d
    \.
    
  • . 不仅是小数点,代表除了\n 以外的任意字符
let reg = /^.$/;
console.log(reg.test('n')); //true
console.log(reg.test('1')); //true
console.log(reg.test('\n')); //false
console.log(reg.test('nn')); //false 只能是一位
复制代码
  • ^ 以某个元字符开头
  • $ 以某个元字符结尾
  • x|y|z x或者y中的任意一个(x和y是自定义的字母)
  • [xyz] x或者y或者z中的任意一个(xyz为自定义)
  • [^xyz] 非x\y\z的任意字符
  • [a-z] 获取a-z中的任意一个字符 [0-9] <=> \d
  • [^a-z] 非a-z的任意字符
  • () 正则分组
  • (?:) 当前正则只匹配不捕获
  • (?=) 正向预查
  • (?!) 负向预查

2.量词元字符:让其左边的元字符出现多少次

*
?
+
{n}
{n,}
{n,m}
// *可以出现0到多次
let reg = /\d*/;
console.log(reg.test('')); //true 
console.log(reg.test('52246')); //true 
复制代码
//+ 可以出现1到多次,但是不能一次都不出现
let reg = /\d+/;
console.log(reg.test('')); //false 
console.log(reg.test('2')); //true
复制代码
// ?可以出现0-1次
let reg = /^\d?$/; //?可以出现0-1个字符
console.log(reg.test('')); //true
console.log(reg.test('5')); //true
console.log(reg.test('5262')); //false
复制代码
//{n}可以出现n个字符
let reg = /^\d{2}$/; 
console.log(reg.test('')); //false
console.log(reg.test('2')); //false
console.log(reg.test('26')); //true
console.log(reg.test('2956')); //false
复制代码
//{n,}可以出现n到多个字符
let reg = /^\d{2,}$/;
console.log(reg.test('')); //false
console.log(reg.test('5')); //false
console.log(reg.test('861')); //true
复制代码
//{n,m}可以出现n次到m个字符
let reg = /^\d{2,4}$/; 
console.log(reg.test('')); //false
console.log(reg.test('5')); //false
console.log(reg.test('52')); //true
console.log(reg.test('946')); //true
console.log(reg.test('9411')); //true
console.log(reg.test('55555')); //false
复制代码

3. 普通元字符

只要在正则中出现的元字符(在基于字面量方式创建),除了特殊和有量词的以外,其余的都是普通元字符

四、 加 ^$ 和不加 ^$ 的区别

let reg = /\d+/; //=> 不加^和$,代表字符传中只要包含xxx即可
console.log(reg.test('kl55ijs56k')); //true 包含0-9之间的任意数字
复制代码
let reg1 = /^\d+$/; //=> 加^和$,代表字符传中必须以0-9中的任意数字为开头和结尾
console.log(reg1.test('kl55ijs56k')); //false
console.log(reg1.test('5222')); //true
复制代码

二、[]的细节 & ()的作用

一、中括号的一些特殊细节

1.[]中出现的元字符一般都是代表本身含义的

let reg = /^[.]+$/; //在这里只是小数点
console.log(reg.test('n')); //false
console.log(reg.test('1')); //false
console.log(reg.test('\n')); //false
console.log(reg.test('nn')); //false
console.log(reg.test('.')); //true
复制代码

但也有特殊情况,例如:

let reg = /^[\d]+$/; //在这里依然是表示0-9之间的任意数字
console.log(reg.test('2')); //true
console.log(reg.test('lks')); //false
复制代码

2.中括号出现的两位数并不是两位数,而是两个数字中的任意一个

let reg = /^[18]$/; //只能是1或者8
console.log(reg.test('1')); //true
console.log(reg.test('8')); //true
console.log(reg.test('18')); //false

let reg1 = /[18]/; //包含1或者8就可以
console.log(reg1.test('1')); //true
console.log(reg1.test('8')); //true
console.log(reg1.test('18')); //true
console.log(reg1.test('958524')); //true
复制代码
let reg = /^[13-56]$/; // 不是13~56 而是1或者3~5或者6
console.log(reg.test('1')); //true
console.log(reg.test('8')); //true
console.log(reg.test('6')); //false
复制代码
// 一位数是正常的 从n到m
let reg = /^[2-5]$/;
console.log(reg.test('2')); //true
console.log(reg.test('8')); //false
console.log(reg.test('5')); //true
复制代码

=======

匹配18-65之间的方法:

18-19 1[89] 20-59 [2-5]\d 60-65 6[0-5]

let reg = /^( ( 1[89] ) | ( [2-5]\d ) | ( 6[0-5] ) ) $/; console.log(reg.test('19')); //true

需求:编写一个规则,匹配"[object AAA]"

let reg = /^[object .+]$/; console.log(reg.test('[object AAA]')); //true

计算字符串中的字节长度(一个英文字节为1,一个中文为2)

let str = '这个case非常international';
let num = 0;
for (let item of str) {
    //[\u4e00-\u9fa5] 中文区间 如果字符串中的各项为中文(即为true)
    if (/[\u4e00-\u9fa5]/.test(item)) {
        num += 2;
    }else{
        num += 1;
    };
};
复制代码

二、()分组的作用

  • 改变默认的优先级
  • 分组捕获
  • 分组引用

1)改变默认优先级(提权)

//不加小括号的情况
let reg = /^12|14$/; // 12或者14或者以12开头或者以14结尾的都可以
console.log(reg.test('12')); //true
console.log(reg.test('14'));//true
console.log(reg.test('1214'));//true
console.log(reg.test('1258465414'));//true
console.log(reg.test('914'));//true
console.log(reg.test('123'));//true
console.log(reg.test('12ko'));//true
console.log(reg.test('+kj14'));//true
console.log(reg.test('12dsjjkn14'));//true
复制代码
let reg = /^(12|14)$/; // 分组改变默认优先级
console.log(reg.test('12')); //true
console.log(reg.test('14'));//true
console.log(reg.test('1214'));//false
console.log(reg.test('1258465414'));//false
console.log(reg.test('914'));//false
console.log(reg.test('123'));//false
console.log(reg.test('12ko'));//false
console.log(reg.test('+kj14'));//false
console.log(reg.test('12dsjjkn14'));//false
复制代码
let str = '2018---4//---/26//';
let reg = /(\d+)\D+(\d+)\D+(\d+)\D+/
let s = str.replace(reg,function($0,$1,$2,$3){
    console.log($0); // 2018---4//---/26//
    console.log($1); //2018
    console.log($2); //4
    console.log($3); //26
    return `${$1}年${$2}月${$3}日`
});
console.log(s); //2018年4月26日
复制代码

关于replace的两个参数:请查看文末关于replace的内容

2)分组引用

let reg = /^([a-z])([a-z])\2\1$/; //正则中出现的\1代表和第一个分组出现一模一样的内容
console.log(reg.test('oppo')); //true
console.log(reg.test('poop')); //true
复制代码

编写一个方法:判断字符串中那个数字最多,出现多少次

let str = '437826357384623537463363726573384633278467382';
let min = -Infinity; //定义一个负无穷大的数
str = str.split('').sort().join('');
// 把乱序字符串用空字符串分割成数组,排序数组,再转成字符串,此时字符串为 '222223333333333333444445556666666777777788888'
/* 
    \d 1个数字 
    \d\1 2个一样的数字
    \d\1+ 2个到多个一样的数字
    /(\d)\1+/g 没找到2个以上一样的数字,函数就执行一次
*/
str.replace(/(\d)\1+/g, function ($0, $1) {
    // console.log($0); //22222  3333333333333  44444  555  6666666  7777777  88888
    // console.log($1); // 2 3 4 5 6 7 8
    if (min < $0.length) {
        min = $0.length;
        let index = $1
    };
})
console.log(min, index); // 13 "3"
复制代码

3)分组捕获

正则捕获使用字符串的 match 方法(正则exec方法不常用)

?:
let str = '2019';
let reg = /(\d)+/;
console.log(str.match(reg)); 
// ["2019", "9", index: 0, input: "2019", groups: undefined] 
// 不仅把大正则的信息捕获到,子项的信息也捕获到
//注意:子项如果包了一个规则,在规则之后有量词,那么结果为最后一个
复制代码

编写一个正则匹配身份证号码

let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;
console.log(reg.exec('220802199603241523'));

//数组:["220802199603241523", "220802", "1996", "03", "24", "2", "3", index: 0, input: "220802199603241523", groups: undefined]
复制代码

三、常用的正则表达式

一、有效数字:

  • 可以出现 + - 号,可以没有,也可以有一个
  • 整数 0 12 9 :一位或者多位数字,一位 0-9 ,多位数字不能以 0 开头
  • 小数部分:可能有可能没有,小数点后面至少要有一位数字
//利用正则检测有效数字
let reg = /^[+-]?(\d|[1-9]\d)(\.\d+)?$/
复制代码

二、手机号码

  • 11位数字
  • 以1开头
let reg = /^1\d{10}$/;
复制代码

三、中文姓名 [\u4E00-\u9FA5]

[\u4E00-\u9FA5]{2,}
//中文姓名 1)中文汉字[\u4E00-\u9FA5]{2,} 2)
let reg = /^[\u4E00-\u9FA5]{2,}((·)([\u4E00-\u9FA5]{2,}))?$/;
console.log(reg.test('汤姆')); //true

console.log(reg.test('汤姆·')); //false
console.log(reg.exec('汤姆·')); // null

console.log(reg.test('汤姆·汉克斯')); //true
console.log(reg.exec('汤姆·汉克斯'));
复制代码

四、邮箱

  • 数字 字母 下划线 - .
  • .- 不能作为开头,不能连续出现 - 或者 .
  • @ 后面(域名): xxx.xx.xx xxx.xx xxx.xx.xx.xx xxx-xxx-xx.xx.xx
let reg = /^\w+([-.]\w)*@[A-Za-z0-9]+([.-])[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)$/
复制代码

四、正则捕获的懒惰性

  • 正则匹配:写一个规则验证当前这个字符串和规则是不是匹配 test()
  • 正则捕获:把一个字符串中所有和正则相匹配的字符获取到 => exec() test()
  • 字符串中的一些方法也能实现正则捕获: match split replace

一、正则捕获的懒惰性

基于 exec 可以实现正则捕获

  • 1.如果当前字符串和正则不匹配 => null
  • 2.如果匹配,捕获的结果是一个数组
    0
    index
    input
    
  • 3.执行一次 exec 只能捕获到第一个和正则匹配的内容,其余匹配的内容还没有捕获到,而且执行多次也无效 => 正则捕获的懒惰性

正则捕获的懒惰性:只能捕获到第一个匹配的内容,剩余的默认捕获不到

let str = 'hello2018hello2019';
let str1 = 'hellohello';
let reg = /\d+/;

console.log(reg.test(str)); //true
console.log(reg.test(str1)); //false
console.log(reg.exec(str1)); // null
console.log(reg.exec(str)); //["2018", index: 5, input: "hello2018hello2019", groups: undefined]
复制代码

二、正则懒惰性产生的原因

lastIndex
lastIndex
let str = 'zhufeng2018zhufeng2019';
let reg = /\d+/;

console.log(reg.exec(str)); //=> 2018
console.log(reg.lastIndex); //=> 0
console.log(reg.exec(str)); //=> 2018
console.log(reg.lastIndex); //=>0

// 即使手动修改了lastIndex值 依旧无效
reg.lastIndex = 11;
console.log(reg.lastIndex); //=>0
复制代码

三、正则懒惰性的解决方案

// 解决正则懒惰性:加全局修饰符g
let str = 'zhufeng2018zhufeng2019';
let reg = /\d+/g;
console.log(reg.lastIndex); //=> 0
console.log(reg.exec(str)); //=> 2018
console.log(reg.lastIndex); //=> 11
console.log(reg.exec(str)); //=> 2019
console.log(reg.lastIndex); //=>22
console.log(reg.exec(str)); //=> null
console.log(reg.lastIndex); //=>0
console.log(reg.exec(str)); //=> 2018
复制代码

四、编写一个方法:执行一次即可获取

let str = 'zhufeng2018zhufeng2019yangfan2020qihang2021';
let reg = /\d+/g;

RegExp.prototype.myExecAll = function(str){
    // 为了防止出现死循环,我们检测一下正则是否加g,没有加g只把第一次捕获的结果返回即可
    if(!this.global){
        return this.exec(str);
    };
    //this:reg 当前操作的正则
    //执行exec开始捕获,具体捕获多少次不定,但是一直到捕获不到内容(null)为止,期间把捕获到的内容存储到数组中
    let result = [];
    let valAry = this.exec(str);
    while(valAry){ //=>this.lastIndex < str.length
        result.push(valAry[0]); //=>把每次捕获到的结果第一项(具体捕获的内容)存储到容器中
        valAry = this.exec(str);
    };
    return result;
};
console.log(reg.myExecAll(str));
//["2018", "2019", "2020", "2021"]
复制代码

五、利用字符串match方法,把捕获到的字符串统一存储到一个数组中

let str = 'zhufeng2018zhufeng2019yangfan2020qihang2021';
let reg = /\d+/g;
console.log(str.match(reg)); 
//["2018", "2019", "2020", "2021"]
复制代码

字符串方法: match

实现了自己编写的execAll处理的事情,但也要加全局修饰符g,把所有匹配的内容都捕获到,最后统一存储到一个数组中返回

五、正则捕获的贪婪性

一、分组捕获

match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。

match 实现了自己编写的 execAll 处理的事情,但也要加全局修饰符 g ,把所有匹配的内容都捕获到,最后统一存储到一个数组中返回

let str = 'zhufeng{2018}zhufeng{2019yangfan{2020}qihang{2021}';
let reg = /\{(?:\d+)\}/g; //大括号有特殊含义:{n}出现的次数
console.log(reg.exec(str));
//["{2018}", "2018", ...]

console.log(str.match(reg)); //=>{2018}", "{2019}", "{2020}", "{2021} 
复制代码
  • 在正则捕获时,如果正则中存在分组,捕获的时候不仅把大正则匹配到的字符捕获到(数组第一项),而且把小分组匹配的内容也单独抽取出来(数组中的第二项开始就是小分组捕获的内容) => 分组捕获
  • /\{(?:\d+)\}/g => ?: 是用来阻止分组捕获内容的 “只匹配不捕获”

match方法的局限性:

console.log(str.match(reg)); //=>{2018}", "{2019}", "{2020}", "{2021}

match方法也有自己的局限性,在正则设置了 g ,基于 match 捕获的内容只有大正则匹配的,小分组的内容没有单独抽取出来

二、正则捕获的贪婪性

每一次匹配捕获的时候,总是捕获到和正则匹配中最长的内容,例如: 2 符合 \d+ 2018 也符合 \d+ ,但是捕获的是最长的内容 2018

let str = 'zhufeng2018zhufeng2019';
let reg = /\d+/g;
console.log(reg.exec(str)); //["2018", ...]
复制代码

解决方案:在量词元字符后面加 ? , 代表的不是出现0-1次,而是取消正则捕获贪婪性

let str = 'zhufeng2018zhufeng2019';
let reg1 = /\d+?/g;
console.log(reg1.exec(str)); // ["2", ...]
复制代码

三、 ? 在正则中的作用

  • 1.量词元字符:出现0-1次 /-?/ 让减号出现或者不出现
  • 2.取消贪婪性: /\d+?/ 只捕获最短匹配内容
  • 3. ?: 只匹配不捕获
    1. ?= 正向预查
    1. ?! 负向预查

六、更多的捕获方式

一、关于 test()exec()

  • 基于 test 进行匹配的时候,如果设置了 gtest 匹配也相当于捕获,修改了 lastIndex 的值
let str = 'zhufeng2018zhufeng2019';
let reg = /\d+/g;
console.log(reg.test(str)); //true
console.log(reg.lastIndex); //11 
console.log(reg.exec(str));//["2019",...]
复制代码
let str = 'zhufeng2018';
let reg = /\d+/g;
console.log(reg.exec(str));//[2018] 把reg.lastIndex值修改了
console.log(reg.exec('zhufeng2018zhufeng2019')); //[2019] 虽然捕获的不是同一个字符串,但是正则是同一个,上一次正则处理的时候修改了它的lastIndex,也会对下一次匹配的字符串产生影响
复制代码

二、捕获方式一:RegExp.$1方式

  • RegExp.$1 把上一次匹配(test/exec)到的结果获取到,获取的是第一个小分组匹配的内容,大正则匹配的内容无法获取,他是一个全局的值,浏览器中$1只有一个,其他的正则操作也会覆盖这个值,所以这种方式一般不用
let str = 'zhufeng2018peixun2019';
let reg = /(\d+)/g;
console.log(reg.test(str)); //true
console.log(RegExp.$1); //2018 

console.log(reg.test(str)); //true
console.log(RegExp.$1); //2019
console.log(reg.test(str));//false
console.log(RegExp.$1); //2019
console.log(reg.test(str));//true
console.log(RegExp.$1); //2018
复制代码

三、捕获方式二:replace

replace:实现正则捕获的方法(本身是字符串替换)

// 需求:把所有的'zhufeng'替换成'zhufengpeixun'
let str = 'zhufeng2018zhufeng2019';
let reg = /(\d+)/g;
str = str.replace('zhufeng','zhufengpeixun'); //一次只能替换一个,第二次还是从索引[0]开始替换
复制代码
  • 真实项目中,需要基于正则来替换
let str = 'zhufeng2018zhufeng2019';
str = str.replace(/zhufeng/g,'zhufengpeixun');
console.log(str); //zhufengpeixun2018zhufengpeixun2019
复制代码

四、replace原理

//不常用
let str = 'zhufeng{val:2018}zhufeng{val:2019}';
reg = /\{val:(\d+)\}/g;

str = str.replace(reg,'#'); // 用reg正则和str字符进行匹配,匹配几次就替换几次,每一次都是当前“大正则”匹配的结果用第二个传递的字符串替换掉了

console.log(str); //zhufeng#zhufeng#
复制代码
//不常见
let str = 'zhufeng{val:2018}zhufeng{val:2019}';
reg = /\{val:(\d+)\}/g;

str = str.replace(reg,'$1'); // $1不是拿这个字符替换掉大正则匹配的内容,此处的$1代表第一个分组匹配的内容,等价于 RegExp.$1
console.log(str); //zhufeng2018zhufeng2019
复制代码

原理: string.replace(要替换的字符串||正则,替换成什么||函数);

  • 1.reg和str匹配多少次,函数就被触发执行多少次,而且传递了一些参数信息值
  • 2.每一次arg存储的信息和执行exec捕获的信息相似(内置原理:每一次正则匹配的结果,都把函数执行,然后基于exec把本次匹配的信息捕获到,然后把捕获的信息传递给这个函数)
  • 3.每一函数返回的内容是什么,就把当前大正则匹配的内容替换成什么
//常用
let str = 'zhufeng{val:2018}zhufeng{val:2019}';
reg = /\{val:(\d+)\}/g;
str = str.replace(reg,(...arg) =>{
console.log(arg);
return 'AA';
});
console.log(str);//zhufengAAzhufengAA
复制代码

关于replace中的的两个参数:

  • 当第二个参数为函数的时候(默认情况下)

    • 函数的第一个参数就是每次匹配到的结果
    • 函数的第二个参数index 正则捕获的起始索印
    • 函数的第三个参数input 原始操作的字符串
    • 函数的第四个(及以后)groups 参数undefined
  • 2.如果有小括号提权,则:

    • 函数的第一个参数就是每次匹配到的结果
    • 函数的第二个参数 第一个小括号匹配的内容
    • 函数的第三个参数 第二个小括号匹配的内容
    • ...以此类推,直到所有的小括号匹配完毕
    • 匹配完毕之后的参数依次为index,input,undefined
  • 此函数必须要有return,不然默认为undefined

  • 多个小括号嵌套的情况: /((\d+)\D+(\d+))\D+(\d+)\D+/

    • 从左向右,第一个外层小括号为第一个,第一个外层小括号内的第一个小括号为第二个,第一个外层小括号内的第二个小括号为第三个,第二个外层小括号为第四个,以此类推
let str = '2018---4//---/26//';
let reg = /(\d+)\D+(\d+)\D+(\d+)\D+/
let s = str.replace(reg,function($0,$1,$2,$3){
    console.log($0); // 2018---4//---/26//
    console.log($1); //2018
    console.log($2); //4
    console.log($3); //26
    return `${$1}年${$2}月${$3}日`
});
console.log(s); //2018年4月26日
复制代码

// 用*替换指定字

let str = '为人民服务,为人民奋斗,为人民努力' 
let s = str.replace(/服务|奋斗|努力/g,function($0,$1){
let temp = '';
for (let i = 0; i < $0.length; i++) {
    temp += '*';
};
console.log($0); //服务 奋斗 努力
return temp
})  
console.log(s); // 为人民**,为人民**,为人民**
复制代码

以上所述就是小编给大家介绍的《JS笔记(15): 正则表达式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

High Performance JavaScript

High Performance JavaScript

Nicholas C. Zakas / O'Reilly Media / 2010-4-2 / USD 34.99

If you're like most developers, you rely heavily on JavaScript to build interactive and quick-responding web applications. The problem is that all of those lines of JavaScript code can slow down your ......一起来看看 《High Performance JavaScript》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

html转js在线工具
html转js在线工具

html转js在线工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试