正则表达式

# 正则表达式

参考链接:正则表达式不要背 (opens new window)

  • regulation expression
  • 计算机能读懂的规则
  • 操作字符串 [TOC]

# 一、写法

let re = /a/;
let re = new RegExp('a');    
let re = /a/i
let re = new RegExp('a','i');
1
2
3
4

# 1.1 转义字符\

大的都不要~(大写的非~)

转义字符 含义
\s 空格 space
\d 数字 digit
\w 字符(字母、数字)word 等价于**[a-zA-Z0-9]**
\b 独立部分 boundary
\S 非空格
\D 非数字
\W 非字符
\B 非独立部分
. 任意字符,不包含空格符
\.
|
* 出现 >= 0
+ 出现 >= 1
出现 0 | 1
^ 正则开始的位置
$ 正则结束的位置
特殊字符 正则表达式 记忆方式
换行符 \n new line
换页符 \f form feed
回车符 \r return
空白符 \s space
制表符 \t tab
垂直制表符 \v vertical tab
回退符 [\b] backspace,之所以使用[]符号是避免和\b重复

# 1.2 标志

  • 可混合使用。
let re = /^[a-zA-Z]/gim;
1
标志 描述
g 全局搜索 global
i 不区分大小写搜索 ignore
m 多行搜索 multiple line
y “粘性”搜索,匹配从目标字符串的当前位置开始

# 二、正则

# 2.1 匹配字符串

正则 成功 失败
re.test(str) true false
str.search(re) 成功的位置 -1
str.match(re) 成功的数组 null
str.replace(re,'new str'/回调函数(匹配成功的字符)) 替换/fn
  • 注意事项

    • replace不会修改原字符串(字符串是只读属性)。
    • search不支持全局匹配
    let str = 'abcdefg';
    str.search(/[d-g]/g);		// 3
    str.match(/[d-g]/g);		// ["d", "e", "f", "g"]
    str.replace(/[d-g]/g,'o');	// "abcoooo"
    
    1
    2
    3
    4

# 2.2 匹配独立项

  • 独立部分:起始位置,结束位置,前面或者后面有一个空格。
  • 隐式位置” \b,匹配这样的位置:它的前一个“显式位置”字符和后一个“显式位置”字符不全是 \w。
let str = 'moon';
alert(/\bm/.test(str));//true
alert(/oon\b/.test(str));//true
1
2
3

# 2.3 匹配子项()

  • 一个正则表达式模式使用括号,将导致相应的子匹配被记住。

# 2.3.1 分组操作

  1. 把正则的整体叫做(母亲)
  2. 把左边第一个小括号里面的正则,叫做这个第一个子项(母亲的第一个孩子)
  3. 第二个小括号就是第二个孩子
let str = '2019-2-14';
let re = /(\d+)(-)/g;

//$0(母亲), $1(第一个孩子), $2(第二个孩子)
str = str.replace(re, function($0, $1, $2) {
    console.log('0:'+$0);
    console.log('1:'+$1);
    console.log('2:'+$2);
    return $1 + '.';//等价于return $0.substring(0, $0.length - 1) + '.';
});

alert(str);//2019.2.14
1
2
3
4
5
6
7
8
9
10
11
12
打印结果:
02019-
12019
2-
02-
12
2-
1
2
3
4
5
6
7

当match不加g的时候才可以获取到子项的集合。

let str = 'abc';
let re = /(a)(b)(c)/;
alert( str.match(re) );  //[abc,a,b,c]

let str = 'abc';
let re = /(a)(b)(c)/g;
alert( str.match(re) );  //abc
1
2
3
4
5
6
7

# 2.3.2 回溯引用

  1. \1 : 重复的第一个子项
  2. \2 : 重复的第二个子项
let str = 'abcb';
let re = /(a)(b)(c)\2/;
alert( re.test(str) );//true
1
2
3
  1. 正向/反向查找
// 正向查找
a(?=b)	// a的后缀b
a(?!b)	// a的后缀不是b

/lin(?=hui)/.test('linhui');	// true
/lin(?!hui)/.test('linjuan');	// true

// 反向查找(正向的中间加小于号)
// 存在兼容问题
// 兼容方法:字符串翻转 + 正向 + 字符串翻转
(?<=a)b	// b的前缀是a
(?<!a)b	// b的前缀不是a
1
2
3
4
5
6
7
8
9
10
11
12
回溯查找 正则 记忆方式
引用 \0,\1,\2 和 $0, $1, $2 转义+数字
非捕获组 (?😃 引用表达式(()), 本身不被消费(?),引用(😃,可提高性能

# 2.4 量词{}

{x}	// 出现x次
{min,max}	// 出现min-max次
{min,}	// 至少min次
{0,max}	// 至多max次
1
2
3
4

# 2.5 字符类[]

  • 能够匹配包含在中括号中的一系列字符中的任意一个,但是匹配的结果只能够是其中的一个而不是多个
  • ^写在[]里面:排除
  • -:确定一个匹配的范围
    • 连字符也是有原则的:前后两个字符是有顺序的,如果使用相同的编码,后面的字符码位应大于或等于前面字符的码位
    • [0-9]//ture
    • [9-0]//false
let str = 'abdc';
let re = /a[bde]c/;
alert(re.test(str));//false
1
2
3

# 三、常见正则表达式

# 3.1 表单校验

let re = {

    email: /^\w+@[a-z0-9]+(\.[a-z]+){1,3}$/,

    // 最短6位,最长16位 {6,16}
    // 可以包含小写大母 [a-z] 和大写字母 [A-Z]
    // 可以包含数字 [0-9]
    // 可以包含下划线 [ _ ] 和减号 [ - ]
    password: /^[\w_-]{6,16}$/

};
1
2
3
4
5
6
7
8
9
10
11
正则 含义
[a-zA-Z] 英文字母
[\u4e00-\u9fa5] 中文
^\s*|\s*$ 行首或行尾空格
^\w+@[a-z0-9]+(\.[a-z]+){1,3}$ Email
[a-zA-z]+://[^\s]* 网址
[1-9][0-9]{4,9} QQ号
[1-9]\d{5} 邮政编码
[1-9]\d{14}|[1-9]\d{17}|[1-9]\d{16}x 身份证

Email:起始至少为一个字符(\w字母,数字或者下划线),然后匹配@,接着为任意个字母或者数字,.代表真正的点,.后面为至少一个的字符(a-z),同时这个(比如.com)整体为一个子项作为结束,可以出现1-3次。因为有的邮箱是 xxxx.@qq.com xxxx.@163.com xxxx.@16.cn.net

# 四、小案例

# 4.1 敏感词的过滤

let str1 = '我过敏的事过了,词';
let re = /过敏|过|词/g;
let str2 = '';

str2 = str1.replace(re, '*'); //我*的事*了,*
1
2
3
4
5

# 4.2 获取class的方法

<body>
    <ul>
        <li class="box1">111</li>
        <li>111</li>
        <li class="box1box2">111</li>
        <li>111</li>
        <li class="box1 box2">111</li>
    </ul>
</body>
1
2
3
4
5
6
7
8
9

只有第一行变红

<script>
    window.onload = function() {
    let aLi = getByClass(document, 'box1');
    for (let i = 0; i < aLi.length; i++) {
        aLi[i].style.background = 'red';
    }
    function getByClass(oParent, sClass) {
        let arr = [];
        let aEle = oParent.getElementsByTagName('*');
        for (let i = 0; i < aEle.length; i++) {
            if (aEle[i].className == sClass) {
                arr.push(aEle[i]);
            }
        }
        return arr;
    }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

第一行和第五行都变红

当正则需要传参的时候,一定要用构造函数的写法

function getByClass(oParent,sClass){
    let arr = [];
    let aEle = oParent.getElementsByTagName('*');

    let re = new RegExp('\\b'+sClass+'\\b');

    for(let i=0;i<aEle.length;i++){
        if( re.test(aEle[i].className) ){
            arr.push( aEle[i] );
        }
    }
    return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4.3 找重复项最多的字符和个数

let str = 'ahdkfhjasjkhdfjalghlsak';
let arr = str.split('');
str = arr.sort().join(''); //aaaaddffghhhhjjjkkkllss

let value = [];
let index = 0;

let re = /(\w)\1+/g;

str.replace(re, function($0, $1) {
    if (index < $0.length) {
        index = $0.length;
        value = $1.split('');
    } else if (index == $0.length) {
        value.push($1);
    }
});
alert('最多的字符:' + value + ',重复的次数:' + index); //最多的字符:a,h,重复的次数:4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 4.4 去掉前后空格

let str = ' hello ';
alert( '('+trim(str)+')' );//(hello)

function trim(str){
    let re = /^\s+|\s+$/g;
    return str.replace(re,'');
}
1
2
3
4
5
6
7

# 4.5 匹配成对标签转驼峰匹配

let str = 'border-bottom-color';
let re = /-(\w)/g;
str.replace(re,($0,$1) => {
    return $1.toUpperCase();
});						// "borderBottomColor"
1
2
3
4
5

# 4.6 验证密码密度

//密码强度 :  数字  小写字母  大写字母
let str = 'AAAaaa1111';

let re1 = /\d/g;
let re2 = /[a-z]/g;
let re3 = /[A-Z]/g;

if( re1.test(str) && re2.test(str) && re3.test(str) ){
    alert('困难');
}
else if((re1.test(str) && re2.test(str)) || (re1.test(str) && re3.test(str))  || (re2.test(str) && re3.test(str)) ){
    alert('普通');
}
else if(re1.test(str) || re2.test(str) || re3.test(str)){
    alert('简单');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 4.7 匹配用户名和限制位数

//匹配用户名 : 汉字/字母/数字 (5-10位)
//必含字母,可能含汉字数字
let str = 'asfasf';

let re = /[a-z]/g;
let re2 = /[\u4e00-\u9fa50-9]?/g;

if( re.test(str) && re2.test(str) && str.length >= 5 && str.length <= 10 ){
    alert('正确');
}
1
2
3
4
5
6
7
8
9
10

# 4.8 数字千分位写法

  • 带有小数点
num.toLocaleString()
1
  • 不带小数点
num.toString().replace(/(\d)(?=(?:\d{3})+$)/g,'$1,')
1
  • 封装
function numFormat(num) {
    if (num.toString().indexOf('.') !== -1) {
        return num.toLocaleString();
    } else {
        return num.toString().replace(/(\d)(?=(?:\d{3})+$)/g,'$1,');
    }
}
1
2
3
4
5
6
7

# 4.9 格式化金钱

const ThousandNum = num => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
const money = ThousandNum(19941112);
// money => "19,941,112"
1
2
3

# 4.10 手机号

// 首位为1,第二位为3、4、5、7、8,后九位为任意数字
let re = /^1[3|4|7|8]\d{9}$/
1
2

# 4.11 大于0的正数

要排除的情况有0,0123,非负数,0.0,0.1,0.1023。

第一种情况:首位是0,但第二位必须是小数点,小数点有可能是0。

/^0\.\d*|[1-9]+)$/
1

第二种情况:首位不是0,但最后一位不是小数点。

/^[1-9][\.\d]\d+$/
1

合并一下

/(^0\.\d{0,}[1-9]+$)|(^[1-9][\.\d]\d+$)/
1