2017 百度前端技术学院——正则表达式入门

2,321 阅读7分钟
原文链接: www.jianshu.com

任务效果预览
源码地址

一、创建一个正则表达式

1 字面量方式:

var expression = /pattern/flags

其中的pattern可以是任何简单的或者复杂的正则表达式,可以包含字符类、限定符、分组、向前查找以及反向引用。每个正则表达式都可带有一个或者多个flags.

正则表达式的匹配模式支持下列3个标志:

  • g:表示全局模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止

例如:

var regex = /b/;
var str = 'abba';
regex.test(str); // true
regex.test(str); // true
regex.test(str); // true

var regex = /b/g;
var str = 'abba';
regex.test(str); // true
regex.test(str); // true
regex.test(str); // false
  • i:表示不区分大小写模式,即在确定匹配项时忽略模式与字符串的大小写

例如:

  /*匹配第一个"bat""cat",不区分大小写*/
var pattern2 = /[bc]at/i/
  • m:表示多行模式,即在到达一行文本末尾时还会继续查找下一行中是否存在于模式匹配的项;会修改^$的行为。默认情况下(即不加m修饰符时),^$匹配字符串的开始处和结尾处,加上m修饰符以后,^$还会匹配行首和行尾,即^$会识别换行符(\n

例如:

/world$/.test('hello world\n') // false
/world$/m.test('hello world\n') // true
注:模式中使用的所有元字符都必须转义。正则表达式中的元字符包括:
( [ { \ ^ $ | ) ? * + . ] }

这些元字符在正则表达式中都有一种或多种特殊用途,因此如果想要匹配字符串中包含的这些字符,就必须对他们进行转义

例如:

/*匹配第一个"bat"或"cat",不区分大小写*/
var pattern = /[bc]at/i

/*匹配第一个[bc]at,不区分大小写*/
var pattern  = /\[bc\]at/i

2 调用RegExp对象的构造函数

 var re = new RegExp("patern","flags")

例如:

  字面量方式:
/*匹配第一个"bat"或者"cat",不区分大小写*/
var pattern  = /[bc]at/i
  构造函数方式:
/*匹配第一个"bat"或者"cat",不区分大小写*/
var pattern  = new RegExp("[bc]at","i");
注:传递给RegExp构造函数的两个参数都是字符串(不能把正则表达式字面量传递给RegExp构造函数)。由于RegExp构造函数模式参数是字符串,所以在某些情况下要对字符进行双重转义。(例如:字符\在字符串中通常被转义为\,而在正则表达式字符串中就会变成\\)

例如:

字面量模式:
 /\[bc\]at/
等价的字符串:
"\\[bc]at"
 字面量模式:
 /\w\\hello\\123/
等价的字符串:
"\\w\\\\hello\\\\123"

3 两种方法的区别:

第一种方法在编译时新建正则表达式,第二种方法在运行时新建正则表达式。考虑到书写的便利和直观,实际应用中,基本上都采用字面量的写法。

二、正则对象的属性和方法

1 属性

正则对象的属性分成两类。

一类是修饰符相关,返回一个布尔值,表示对应的修饰符是否设置
  • ignoreCase:返回一个布尔值,表示是否设置了i修饰符,该属性只读
  • global:返回一个布尔值,表示是否设置了g修饰符,该属性只读
  • multiline:返回一个布尔值,表示是否设置了m修饰符,该属性只读

例子:

var r = /abc/igm
r.ignoreCase  //true
r.global  //true
r.mutiline  //true
另一类是与修饰符无关的属性
  • lastIndex:表示开始搜索下一个匹配项的字符的位置,从0算起;该属性可读写,只有在设置了g时才有意义
  • source:返回正则表达式的字符串形式,只读
var r = /abc/igm
r.lastIndex //0
r.source //"abc"

var r = new RegExp("abc","igm")
r.lastIndex //0
r.source  //"abc"
注:尽管第一个模式使用的是字面量,第二个使用的构造函数模式,但是source属性相同。可见,source保存的是字面量形式的字符串

2 方法(此方法属于正则对象;即pattern.方法()。注意区别于下面介绍的字符串对象的方法)

  • test()返回一个布尔值,表示当前模式是否能匹配参数字符串
    pattern.test("字符串")
    /cat/.test("vat and dogs") //true

如果正则表达式带有g修饰符,则每一次test方法都从上一次结束位置的后一位向后匹配

var r = /x/g 
var s = '_x_x'
console.log(r.lastIndex); //0
console.log(r.test(s)); //true
console.log(r.lastIndex); //2
console.log(r.test(s)); //true
console.log(r.lastIndex); //4
console.log(r.test(s)); //false
注:如果正则模式是一个空字符串,则匹配所有字符串
new RegExp("").test("abc")
这里不能使用字面量形式,否则会报错!!!
  • exec()方法可以返回匹配结果,如果发现匹配,就返回一个数组,成员是每一个匹配成功的字符串,否则返回null
pattern.exec("字符串")
var s = '_x_x'
var r1 = /x/
var r2 = /y/
console.log(r1.exec(s)); // ['x']
console.log(r2.exec(s)) //null

如果pattern包含分组(即含有圆括号:"()",用来一次匹配多个字符),则返回的数组包含多个成员,第一项是匹配的整个字符串,第二项是第一个分组,第三项是第二个分组,以此类推;真个数组的length属性等于分组的数量+1

var r = /a(b+)a/;
var arr = r.exec('_abbba_aba_');
arr // ["abbba", "bbb"]
arr.index // 1
arr.input // "_abbba_aba_"
 input:整个原字符串
 index:pattern匹配成功的开始位置(从0开始)
 以上两个属性是返回的数组所拥有
注:如果正则模式是一个空字符串,则exec方法返回一个空数组
var r1 = new RegExp('');
var a1 = r1.exec('abc');
console.log(a1); //["", index: 0, input: "abc"]
console.log(a1.index); //0
console.log(r1.lastIndex);  //0
var r1 = new RegExp('()');
var a1 = r1.exec('abc');
console.log(a1); //["", "", index: 0, input: "abc"]
console.log(a1.index); //0
console.log(r1.lastIndex);  //0

三、字符串的模式匹配方法(字符串.方法(pattern) )

  • match():返回一个数组,成员是所有匹配的子字符串,matchexec类似,匹配成功则返回一个数组,否则返回null
注意:两个方法在没有g标识符的情况下表现相同(即返回匹配的真个字符串,以及匹配到的分组);在有g的情况下,match不会返回分组,而exec会返回分组,并且lastIndex属性对match无效
没有g标识符:
var s = 'abba';
var r = /a(b+?)/;
console.log(s.match(r)) // ["ab", "b", index: 0, input: "abba"]
console.log(r.exec(s)) // ["ab", "b", index: 0, input: "abba"]

有g标识符:
var s = 'abba';
var r = /a(b+?)/g;
console.log(s.match(r)) // ["ab"] ,不包括分组,并且没有Index和input属性
console.log(r.exec(s)) // ["ab", "b", index: 0, input: "abba"]

因此,execmatch更强大
设置正则表达式的·lastIndex·属性,对match方法无效,匹配总是从字符串第一个开始

var r = /a|b/g 
r.lastIndex  =7;
console.log('xaxb'.match(r)); //["a","b"]
console.log(r.lastIndex); //0
  • search方法,返回第一个满足条件的匹配结果在原字符串中的位置,如果没有匹配,则返回-1
    "_x_x".search(/x/)
    注:该方法会忽略g修饰符
    var r= /x/g 
    r.lastIndex =2;
    console.log('_x_x'.search(r)) //1
  • replace方法可以替换匹配的值。
    str.replace(正则对象,替换内容)

例如

console.log("aaa".replace(/a/,'b'));  //baa
console.log("aaa".replace(/a/g,'b')); //bbb
若标识符设置了g,则全部替换,否则只替换匹配到的第一个

replace方法第二个参数可以使用美元符号$,用来只带替换的内容

  • $&:指代匹配的子字符串
  • $`:指代匹配的字符串前面的文本
  • $ ':指代匹配的字符串后面的文本
  • $n:指代匹配成功的第n组内容,n是从1开始的
  • ?:指代美元符号
    "abc".replace("b","[$`-$&-$']") // a[a-b-c]c
    "hello world".replace(/(\w+)\s(\w+)/,"$2 $1") //world hello
    replace第二个参数还可以是一个函数,函数接收的第一个参数是捕获到的内容,第二个,第三个....是分组的内容,倒数第二个是捕获内容在原字符串中的位置,最后一个参数是原字符串
    var str = "3 and 5".replace(/[s0-9]+\s+?(and)/,function (match,item1,index,str){
      // return match+'hello'  // 替换后的str = "3 andhello 5"
          // return item1   // 替换后的str = "and 5"
          // return index  // 替换后的str =  "0 5"
             return str  // 替换后的str =  "3 and 5 5"
    })
    console.log(str);
  • split方法可以基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是字符串,也可以是一个RegExp对象(这个方法不会将字符串看成是正则表达式)。split()方法可以接受可选的第二个参数,用于指定数组的大小。
    var colorText = "red,blue,green,yellow"
    var color1 = colorText.split(',');
    console.log(color1); //["red", "blue", "green", "yellow"]
    var color2 = colorText.split(',',2);
    console.log(color2); //["red", "blue"]
    var color3 = colorText.split(/[^,]+/);
    console.log(color3); //["", ",", ",", ",", ""]
    注:如果正则表达式带有括号,则括号匹配的部分也会作为数组成员返回
    console.log("aaa*a*".split(/(a*)/)); //["", "aaa", "*", "a", "*"]

四、基本概念

1、字面量字符和元字符

如果在正则表达式之中,某个字符只表示它字面的含义(就像前面的a和b),那么它们就叫做“字面量字符”

例如:

比如/a/匹配a,/b/匹配b

2、除了字面量字符以外,还有一部分字符有特殊含义,不代表字面的意思。它们叫做“元字符”(metacharacters),主要有以下几个

  • 点字符(.)
    点字符(.)匹配除回车(\r)、换行(\n)、行分隔符(\u2028)和段分隔符(\u2029)以外的所有字符。
注:回车(\r)不一定换行(\n)
  • 位置字符
    ^ 表示字符串的开始位置
    $ 表示字符串的结束位置
    // test必须出现在开始位置
    /^test/.test('test123') // true
    // test必须出现在结束位置
    /test$/.test('new test') // true
    // 从开始位置到结束位置只有test
    /^test$/.test('test') // true
    /^test$/.test('test test') // false
  • 选择符(|)
    选择符会包括它前后的多个字符,比如/ab|cd/指的是匹配ab或者cd,而不是指匹配b或者c
    /ab|cd/.test("abef")  //true
    /ab|cd/.test("cdef")  //true
    如果向改变这个行为可以使用圆括号。
    /a( |\t)b/.test("a\tb") //true
    指匹配a和b之间有一个空格或者是制表符
  • 转义符
    正则表达式中那些有特殊含义的字符,如果要匹配它们本身,就需要在它们前面要加上反斜杠。比如要匹配加号,就要写成\+
    /1+1/.test('1+1') // false
    /1\+1/.test('1+1') // true
    注:需要转义的字符:
    ^、.、[、$、(、)、|、*、+、?、{ 和 \\
  • 字符类
    字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内,比如[xyz] 表示x、y、z之中任选一个匹配
    注:如果方括号内的第一个字符是[^],则表示除了字符类之中的字符,其他字符都可以匹配。

例如:

[^xyz]表示除了`xyz`之外都可以匹配
/[^abc]/.test('hello world') // true
/[^abc]/.test('bbc') // false

如果方括号内没有其他字符,即只有[^],就表示匹配一切字符,其中包括换行符和回车符,而点号(.)是不包括换行符和回车符的。

var s  = "Please yes\nmake my day!"
console.log(s.match(/yes.*day/)); //null
console.log(s.match(/yes[^]*day/)); //["yes↵make my day", index: 7, input: "Please yes↵make my day!"]
console.log(s);  //Please yes
                  make my day!
  • 连字符(-) 表示范围
    例如:
    /a-z/.test('b') // false
    /[a-z]/.test('b') // true
    注:另外,不要过分使用连字符,设定一个很大的范围,否则很可能选中意料之外的字符。最典型的例子就是[A-z],表面上它是选中从大写的A到小写的z之间52个字母,但是由于在ASCII编码之中,大写字母与小写字母之间还有其他字符,结果就会出现意料之外的结果。
    /[A-z]/.test('\\') // true
    上面代码中,由于反斜杠(\ \)的ASCII码在大写字母与小写字母之间,结果会被选中。
  • 预定义模式
    预定义模式指的是某些常见模式的简写方式。
    \d 匹配0-9之间的任一数字,相当于[0-9]。
    \D 匹配所有0-9以外的字符,相当于[^0-9]。
    \w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]。
    \W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]。
    \s 匹配空格(包括制表符、空格符、断行符等),相等于[\t\r\n\v\f]。
    \S 匹配非空格的字符,相当于[^\t\r\n\v\f]。
    \b 匹配词的边界。
    \B 匹配非词边界,即在词的内部。
    例如:
    /\s\w*/.exec('hello world') // [" world"]
    /\bworld/.test('hello world') // true
    /\bworld/.test('hello-world') // true
    /\bworld/.test('helloworld') // false
    //单边边界:指单词的前面或者后面不是\w
    // \B的例子
    /\Bworld/.test('hello-world') // false
    /\Bworld/.test('helloworld') // true
    注:通常,正则表达式遇到换行符(\n)就会停止匹配。
    var html = "<b>Hello</b>\n<i>world!</i>";
    /.*/.exec(html)[0]
    // "<b>Hello</b>"
  • 重复类
    {n}:重复n次
    {n,}:至少重复n次
    {n,m}:至少重复n次,之多重复m次
  • 量词符
    ?:某个模式出现0次或1次,等同于{0,1}
    *:表示某个模式出现0次或多次,等同于{0,}
    +:表示某个模式出现1次或多次,等同于{1,}

五、高级应用

1、贪婪模式:

三个量词符,默认情况下都是最大可能匹配,即匹配直到下一个字符不满足匹配规则为止。这被称为贪婪模式.

例如:

var s = 'aaa';
s.match(/a+/) // ["aaa"]

2、惰性模式:

尽可能少的匹配,一旦匹配即终止

常用惰性模式:
?:表示某个模式出现0次或多次,匹配时采用非贪婪模式。
+?:表示某个模式出现1次或多次,匹配时采用非贪婪模式

例如:

var s = 'aaa';
s.match(/a+?/) // ["a"]

3、组匹配(捕获模式)

也就是一次性匹配多个字符。
例如:

/fred+/.test('fredd') // true
/(fred)+/.test('fredfred') // true

上面代码中,第一个模式没有括号,结果+只表示重复字母d,第二个模式有括号,结果+就表示匹配“fred”这个词。

注:在正则表达式的内部,可以用\n引用括号匹配的内容

例如:

/(.)b(.)\1b\2/.test("abcabc") //true

括号还可以嵌套
console.log(/y((..)\2)\1/.test("yabababab")); //true

4、非捕获组

(?:x)称为非捕获组,表示不返回该组匹配的内容,即匹配的结果中不计入这个括号。

例如:

var m = 'abc'.match(/(?:.)b(.)/);
m // ["abc", "c"]

5、先行断言

x(?=y)称为先行断言,x只有在y前面才匹配,y不会被计入返回结果

例如:

var m = 'abc'.match(/b(?=c)/);
m // ["b"]

5、先行否定断言

x(?!y)称为先行否定断言,x只有不在y前面才匹配,y不会被计入返回结果
例如:

/\d+(?!\.)/.exec('3.14')
// ["14"]

任务代码:
html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>正则表达式入门</title>
    <style type="text/css">
        #wrap{
            width: 500px;
            margin: 100px auto;
        }
    </style>
    <script type="text/javascript" src='index.js'></script>
</head>
<body>
<div id="wrap">
    <h1>验证手机号</h1>
    <input type="text" name="" id="inp" placeholder="请输入电话号码">
    <button type="button" id='btn'>点我验证是否为手机号码</button>
    <h1>验证是否有相邻的重复单词</h1>
    <input type="text" name="" id="inp1" placeholder="请输入英文字母组成的单词">
    <button type="button" id='btn1'>点我验证相邻单词是否重复</button>
</div>

</body>
</html>

js代码:

    /*
            电话号码前三位规则:
                联通:186 185 170 156 155 130 131 132
                移动:139 138 137 136 135 134 178 188 187 183 182 159 158 157 152 150 147
                电信:189 180 170 153 133
                第一位全是1
                第二位:3 4 5 7 8
                第三位:0 1 2 3 4 5 6 7 8 9
         */
        window.onload = function (){
            var oBtn = document.getElementById('btn'),
                oBtn1 = document.getElementById('btn1'),
            oInp = document.getElementById("inp"),
            oInp1= document.getElementById("inp1");

            // 验证手机号
            oBtn.addEventListener('click',function (){
                var oText =oInp.value,
                    re = /^1[34578][0-9]{9}$/;
                    alert(re.test(oText))

            },false)

            // 判断相邻单词是否重复
            oBtn1.addEventListener("click",function (){
                var oText1 = oInp1.value.replace(/^\s+|\s+$/g,''),//先去除输入的整个字符串的前后空格
                    re = /\b([a-zA-Z]+)\s+\1\b/; //使用\b词边界(词边界是非\w)
                    alert(re.test(oText1));
    /*
\b 元字符匹配单词边界。(这里的单词指的是\w匹配的数字、字母、下划线 :[a-zA-Z0-9])

在单词边界匹配的位置,单词字符后面或前面不与另一个单词字符直接相邻。请注意,匹配的单词边界并不包含在匹配中。换句话说,匹配的单词边界的长度为零。

提示:\b 元字符通常用于查找位于单词的开头或结尾的匹配。

另外:"\b"只有在字符组中,它表示的是退格键,即 [a-z\b]
                     */
            })
        }

本文对你有帮助?欢迎扫码加入前端学习小组微信群: