正则表达式入门

前言

俩件事促使我写这篇文章。一则之前面试面试到了正则,发现不甚熟悉。二则这些天看jQuery源码,一些正则看的有点费劲。

准备

先说些题外话。什么是正则表达式?其实正则表达式也叫规则表达式。个人感觉后者比较贴切。也很纳闷Regular Expression为什么会翻译成正则表达式。知道的朋友可以告知下。
其实这个正则就是字符串匹配工具,它描述了字符串的规则。无论是初学者还是老司机相信写起来都费劲,因为是火星文啊有木有。那为什么要学习这个火星文呢?那自然是好处大大啊。
举个栗子。前俩天解单时有个情况是需要对location.hash解析出#后面的第一个字符串(不包括/)。譬如

//"#/notebooks/17855961"解析出来的得是notebooks
// “#notebooks/17855961”解析出来的得是notebooks
function parseHash(str) {
    var hashArr = str.split('/');
    var res;
    if (hashArr[0] === '#') {
        res = hashArr[1];
    } else {
        res = hashArr[0].substring(1);
    }
    return res;
}
function parseHash(str) {
    var reg = /(?<=^(?:#\/|#))[^\/]+?(?=\/)/;
    return str.match(reg)[0];
}

其实,不用正则也是可以的,当然你得像上面一样写一大串代码。

基础

元字符

其实就是正则里具有特殊含义的字符。就比如\d代表数字。
常用元字符

代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始(值得注意的是,它在[]里面指的是非,在外面指的是字符串开始,还可以用在零宽断言)
$ 匹配字符串的结束

反义词

常用反义词
可以看得出好些就是元字符大写就是其对应的负面。每一对可匹配全部。譬如\w\W

代码/语法 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的任意字符

限定符

什么是限定符呢?举个栗子吧。街上很多人。这个呢可以是元字符,而这个许多就是限定符。和元字符或者表达式模式结合使用
常用限定符

代码/语法 说明
* 重复零次或更多次(相当于{0,})
+ 重复一次或更多次(相当于{1,})
? 重复零次或一次(相当于{0,1})
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

转义

如果有时候你要匹配譬如.、*时会发现不能匹配,这是因为它们在正则里有其特殊含义(元字符等)。此时就需要转义。即.和*。
eg. https:\\baidu.com就是https:\baidu.com(瞎写o(╯□╰)o)

类与组以及分支

比如\d匹配数字,已经圈死为数字类。但是比如现在我想匹配自定义的类别,比如我想看看有没有1、2、3之中任何字符那就是[123]。比如[0-9]其实和/d一样。[]就是类,但是可以发现它只是单个字符,所以也就是字符类。
但是现在我想匹配多字符,那就是组了。比如我要匹配yellow、green、red。(yellow|green|red)。这()就是组,后面接限定符可以重复这个组,用法和元字符类似。|就是分支,也就是或的意思。
这里可能有点乱。给个小栗子总结下:
怎么匹配国内的手机号呢?咋们先想想手机号的规则。

  1. 手机号一共11位数字
  2. 前两位只能是13、14、15、18
  3. 有座机的朋友应该知道打外省的手机号需要加拨0(至少我小时候是这样子的_
    自然而然就得出答案:0?(13|14|15|18)[0-9]{9}
    自此,差不多可以应付大多数工作上简单的情况(工作上真心用得少,以为你会发现你用的复杂还怕别人看的痛苦,注释都不好写( ˇˍˇ )。更郁闷的是过几天自己都看不懂)

进阶

现在开始讲些难些的,用于应付阅读源码。

反向引用、捕获组/非捕获组

先举个小例子。比如给定一个字符串,我想先匹配一下一个不定的单词,然后此单词之后得跟着相同的单词。可能有点绕。就是比如前面匹配到了pz,那么紧跟着也得是pz,即pz pz。如果匹配到的时ap,那么紧跟着的也得是ap,即ap ap。
这时候就需要反向引用了。先写前面的匹配,即\b(\w+)\b\s+,之后怎么写呢?很简单:\b(\w+)\b\s+\1\b。这里的\1就是对前面的那个组的引用。
为什么时\1呢?其实每个组都是会分配到一个组号。整个正则表达式匹配到的为分组0,然后从左往右扫,依次分组1、2递增。注意,顺序看(,不要被数学里的表达式优先级给带入坑。比如:(1(2)(3)),顺序如此1、2、3,并非是2、3、1这个顺序
有个情况需要注意的是,正则不是某个语言独有的,所以不同语言的支持情况可能有差异。
PS: JS是不支持给组自定义分配组号的,C#就可以。有兴趣的可以了解了解。对了,若是有自定义组号,分配组号时会扫描俩遍。第一遍是给未自定义组号的组分配组号,第二遍给自定义的组分配组号。
如果组过多的话我不想某些组被捕获,那么可以使用?:。即捕获组为(exp),非捕获组为(?:exp)。非捕获组是不会被分配组号的。也不被捕获文本。(字符串match返回值不包括非捕获组)

代码/语法 说明
(exp) 捕获组:匹配exp,会被分配组号的,被捕获文本
(?:(exp) 非捕获组:匹配exp,不会被分配组号的,也不被捕获文本

有图有真相时间(给本小节所说证明一下)


正则表达式入门_第1张图片
捕获组

正则表达式入门_第2张图片
非捕获组

零宽断言(zero-width assertion)

先行断言、先行否定断言(JS支持)

正则表达式入门_第3张图片

好吧,这就是我第一次知道这货的表情。什么鬼?已经很难明白了,现在连命名都这样子?怎么活。好吧,不急,其实这货意义就是指定位置应该满足指定的条件。还是很无语?


正则表达式入门_第4张图片
  1. 比如匹配小数的整数部分,即:/\d+(?=.)/。
    (?=exp)就是正预测先行断言,也叫先行断言,只匹配exp前面的位置,断言自身位置后面跟着exp
    正则表达式入门_第5张图片

    可以看见的是先行断言是不会被匹配到的,这就是零宽。它只是告诉你是否匹配成功,它不消费任何字符。
  2. 除了先行断言,还有先行否定断言,也就是负预测先行断言。这货是先行断言的否定,看名字就看得出来。
    比如匹配后面没有小数点的数字,即:/\d+(?!.)/g
    (?!exp)就是负预测先行断言,也叫先行否定断言,只匹配后面没有exp的位置,断言自身后面没有exp
    正则表达式入门_第6张图片

后行断言、后行否定断言(目前不支持,但将支持)

其实也不能说不支持,至少此时我试了下我的Chrome(版本 62.0.3202.94)是支持的,不过Firefox是不支持的。

  1. 比如要匹配小数的小数部分,即/\d+(?<=.)/
    (?<=exp)就是后行断言,也叫正回顾后发断言,只匹配前面有exp的位置,断言自身前面有exp
    正则表达式入门_第7张图片
  2. 比如匹配前面没有小数点的数字,即:/(? (?
    正则表达式入门_第8张图片

正则的性情(贪婪模式、懒惰模式)

正则默认情况下是贪婪的。就比如爬过小说的朋友应该知道。不是所有的网站都是通过API请求数据填充网页的。很多网站是服务端渲染,爬的时候就只能获取到整个HTML网页。这时候就需要使用正则把html标签去掉。
有了上面的知识你很快就会写出来: /<.+>/g。字符串replace方法替换成空格即可。很遗憾,这个是不行的,为什么呢?因为正则贪婪

斗破苍穹
炎帝
五帝破空
//这里会匹配到整个html,因为你会发现整个html也符合你写的这个表达式呀

这时候机智的你可能会想到,<>里面是不能有<>的,那我可以这样子:/<[^<>]+>/g。
是的,的确可行。但是你至少得想到这个特殊情况吧,难道以后每次都得想下,很是麻烦啊。其实有简便的方法,那就是限定符后面加个?。即:/<.+?>/g,这就是懒惰模式
很好理解,就是匹配到就不继续了。?本来就是代表0-1嘛(强行解释一波O(∩_∩)O哈哈~)

正则表达式入门_第9张图片

PS: 所以有*?、+?、??、{n, m}?、{n, }?。是没有{n}?滴

修饰符

为什么修饰符放在最后说呢?因为它最不起眼。最简单。
就是加在正则最后面。

修饰符 描述
i ignoreCase执行对大小写不敏感的匹配。
g global执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)

有兴趣可以自行看下多行模式,这里不提。

RegExp对象

既然是前端,自然得来点前端的

属性

其实上面的修饰符也是RegExp属性,比如

/\d/g.ignoreCase  // false
/\d/g.global // true

与修饰符无关的主要有俩

  1. source,返回正则表达式字符串形式
/^\d\\\.[0-9]$/g.source // "^\d\\\.[0-9]$"
  1. lastIndex,返回下一次开始搜索的位置,它是可读写的,注意只有带g修饰符才有意义


    正则表达式入门_第10张图片

方法

  1. test
    返回true or false,表示当前表达式能否匹配当前字符串
  2. exec
    匹配到的话以数组形式返回结果,成员是每一个匹配到的串,否则返回null

后记

最后来个考试。从jQuery拷了个正则解读下

/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/

我看懂了,你们呢?

你可能感兴趣的:(正则表达式入门)