笔记是由油管的@The Coding Train老师发布系列教程。 由于正则我本身看了不少次,可是很快又忘记。因此为了完全搞懂,一边学习一边记笔记,以给别人讲课的方式记笔记,我本身的印象会更深,因此就有了如下内容。 小白的晋级路在我的github会持续更新哦: 传送门,欢迎starjavascript
秋招&面试系列:html
2.VK的秋招前端奇遇记(二)java
4.VK的秋招前端奇遇记(四)github
经过一张图表来对正则表达式的基本进行一个回顾正则表达式
single char | quantifiers(数量) | position(位置) |
---|---|---|
\d 匹配数字 | * 0个或者更多 | ^一行的开头 |
\w 匹配word(数字、字母) | + 1个或更多,至少1个 | $一行的结尾 |
\W 匹配非word(数字、字母) | ? 0个或1个,一个Optional | \b 单词"结界"(word bounds) |
\s 匹配white space(包括空格、tab等) | {min,max}出现次数在一个范围内 | |
\S 匹配非white space(包括空格、tab等) | {n}匹配出现n次的 | |
. 匹配任何,任何的字符 |
假设你有一段字符以下: 算法
\w
将匹配全部word,固然,() - 等字符除外数组
\w\w\w
发现匹配的有'The
se are
som
e pho
ne number
s ...' 注意正则表达式是匹配一个连续串的规则,因此能够看到三个字母的单词能够匹配到,6个单词的也能够匹配到。
\s\s
匹配到一行中连续两个空格
假设咱们有这一段话:
The colors of the rainbow have many colours
and the rainbow does not have a single colour.
复制代码
咱们想把全部的颜色找出来colors
colours
colour
答案 colou?rs?
嗯,看起来很简单,很方便。
好了,如今想要匹配一行中的4个数字,或者一行中的5个字母等,这时候用quantifiers就很是方便了。
我如今想找5个字母组成的单词
\w{5}
这样能够吗?嗯..不行的,看下它匹配的内容,以下: 'These
are some phone
numbe
rs 915-555-1234...' 的确,咱们模板给的很简单,它只找一行中,连续出现5个字母的序列。因此如今改进一下好了
\w{5}\s
为了能找到单词,因此我但愿5个字母后,跟一个空格的序列,这样应该能够了吧,看下匹配状况: 'These
are some phone
numbers
915-555-1234...' 嗯,是的,只有目前这些方法,是作不到的。 因此,咱们须要第三个工具 "position"
回到刚才的问题以前,先熟悉下^
$
和 \b
This is somthing
is about
a blah
words
sequence of words
Hello and
GoodBye and
Go gogo!
复制代码
来看下各类正则所匹配的内容
\w+
这个应该毫无疑问,匹配全部的words
^\w+
多了一个^
,这样子,就只能匹配到每一行开头的单词了This
is
a
words
sequence
Hello
GoodBye
Go
\w+$
这样就能匹配到每行的最后一个字母
回到刚才的问题
如今想找5个字母组成的单词
就变得很简单了,使用单词结界符\b
答案就是\b\w{5}\b
最后,找一个刚才出现的电话号123-456-1231
用以上最基本的正则方法就是 \d{3}-\d{3}-\d{4}
,这样就找到了。 可是有的时候,电话号码是123.456.1234
或者 (212)867-4233
的结构怎么办呢?
正则表达式中的或
或者其余表达方式,下面一一来介绍。
前面记录了最基本的方法,接下来讲一下分类符[]
这个符号用来表示逻辑关系或
,好比[abc]
表示a或者b或c.[-.]
表示符号-
或者.
号(注意这里,在[]
中的.
号表明的就是这个符号,可是若是在其外面,表示个匹配全部。 因此若是不在[]
之中,想要匹配'.',就要经过转意符号\.
)
字符序列:
The lynk is quite a link don't you think? l nk l(nk 复制代码
正则表达式: l[yi (]nk
结果:
lynk link l nk l(nk
复制代码
很容易理解的,就是表达或
逻辑。
好了,如今回到以前遗留的问题,有如下字段,请匹配全部可能的电话号码:
These are some phone numbers 915-134-3122. Also,
you can call me at 643.123.1333 and of course,
I'm always reachable at (212)867-5509 复制代码
好的,一步一步来,刚才咱们使用\d{3}-\d{3}-\d{4}
匹配了连字符的状况。如今咱们能够很轻松的把.
这种状况加进去了
第一步: \d{3}[-.]\d{3}[-.]\d{4}
第二步: 为了可以匹配括号,可使用?来,由于这是一个option选择。因此最后就成了
\(?\d{3}[-.)]\d{3}[-.]\d{4}
这里仍是要说明,在[]中,特殊字符不须要转义,能够直接使用,好比[.()]
,可是在外面,是须要转义的\(
\.
等
刚才介绍了最简单和基本的功能,可是有些特殊的地方须要注意
好比[-.]
的含义是连字符-
或者点符.
。 可是,若是当连字符不是第一个字符时,好比[a-z]
,这就表示是从字母a到字符z。
^
在以前介绍中,是表示一行开头,可是在[]
中,有着不一样的含义。 [ab]
表示a或者b [^ab]
啥都行,只要不是a或b(anythings except a and b),至关于取反
除了使用[]
表示或逻辑,()
也是能够的。用法是(a|b)
表示a或者b
好比下面的例子,匹配全部email
gaoyaqi411@126.com
dyumc@google.net
sam@sjtu.edu
复制代码
思路:
首先要想我到底相匹配什么,这里我想匹配的是
\w+
@
符号 \w+@
\w+@\w+
.
标点 \w+@\w+\.
com
net
或 edu
\w+@\w+\.(com|net|edu)
仍是提醒注意第四步的\.
转义符号
好了,这样几能够匹配以上的全部邮箱了。可是还有一个问题,由于邮箱用户名是能够有.
的,好比vincent.ko@126.com
其实仍然很简单,修复以下: [\w.]+@\w+\.(com|net|edu)
[]
的做用,用英文表达就是"alternation",表达一个或的逻辑;/[-.(]/
在符号中的连字符-
放在第一位表示连字符自己,若是放在中间,表示"从..到..",好比[a-z]
表示a-z[.)]
括号中的特殊符号不须要转义,就表示其自己[^ab]
括号中的^
表示非,anythings except a
and b
(a|b)
也可表示选择,可是它有更强大的功能....因此,()
的强大功能是什么呢? 分组捕获,这对序列的替换、交换是颇有帮助的。 后面一节进行学习记录
什么是分组捕获,如今回到以前电话号码的例子
212-555-1234
915-412-1333
//我想要保留区号,把后面的电话号码变为通用性的
👇👇👇👇👇👇👇👇👇👇👇👇
212-xxx-xxxx
915-xxx-xxxx
复制代码
按照以前的作法\d{3}-\d{3}-\d{4}
,这种匹配的方式,是将整个电话号码做为一个组(group)匹配起来。 咱们把212-555-1234
这样的叫Group0
。
这个时候,若是咱们加了一个括号\d{3}-(\d{3})-\d{4}
,那么匹配到的555
就叫Group1
。 以此类推,若是有两个小括号\d{3}-(\d{3})-(\d{4})
那么分组就是下面的状况:
212-555-1234 Group0
555 Group1
1234 Group2
复制代码
如今组已经分好,那么如何选择已经匹配的分组?
这里有两种方法,第一种使用$
符号,好比$1
表明555
,$2
表明1234
;第二种,使用\
,好比\1
表明555
。两种的使用场景不同,先讲$
如今为了知足最开始的要求,咱们能够这么作
reg: \(?(\d{3})[-.)]\d{3}[-.]\d{4}
replace: $1-xxx-xxxx
复制代码
ps: 这里能够直接用JS的replace函数进行操做,可是正则不是JS专属的,因此这里先介绍通用方法,以后对JS部分进行总结
shiffina, Daniel
shifafl, Daniell
shquer, Danny
...
复制代码
实现方法:
reg: (\w+),\s(\w+)
replace: $2 $1
复制代码
注意:$0
是全部匹配到的,因此第一个加括号的是$1
[google](http://google.com)
[itp](http://itp.nyu.edu)
[Coding Rainbow](http://codingrainbow.com)
复制代码
解析: 这道题有些坑,须要慢慢来。
看到这个,第一个想考虑匹配[google]
这个东西,立马想到正则表达式\[.*\]
。 这个是巨大的坑,在当前来看,它的确能正确匹配到上面的三条。 可是若是文本是这样的:
看到了,第一行的内容会所有匹配下来,而不能区分[google]
和[test]
。 之因此这样,是由于.
是贪婪的,他表示全部,全部能匹配到的,因此固然也包括了]
,一直到这一行的最后一个]
,它才中止。
因此为了让它能正确匹配,须要去掉这种贪婪的属性。这里用到?
。 当?
放在了quantifiers
符号后,表示去掉贪婪属性,匹配到终止条件,便可停下。
\[.*?\]
这样子,就能够将[google]
和[test]
分开,效果以下:
接下来完成全部内容:
reg: \[(.*?)\]\((http.*?)\)
replace: <a href="$2">$1</a>
复制代码
\
选择器$
选择符是在替换的时候进行的标志或选择,可是若是在正则表达式自己,就要使用\
选择了。好比如下的场景
This is is a a dog , I think think this is is really
a a good good dog. Don't you you thinks so so ? 复制代码
咱们想要匹配好比is is
so so
这样连续的序列,就用到了下面的表达方式: (\w+)\s\1
效果:
嗯,差很少达到效果,可是有一些小的bug。好比第一句话This is is a
这个就匹配不许确,会把第一个This的后面字母匹配进去。 这就用到第一节说的字符结界 \b
了,就变成了\b(\w+)\s\1\b
好了,大功告成,就不贴效果图了,自行脑补就行了。
$1
和\1
,可是使用场景不一样,\
用在正则表达式本身身上?
符号能够禁止贪婪属性,放在.*
以后,表示一次匹配遇到重点就能够中止。不然将会一直向后匹配。在js中,主要的正则表达式都是涉及到string的应用。
var str = "hello"
var r = /w+/
复制代码
这两个分别是string和reg的字面量建立方法。当要使用正则来进行操做的时候,使用了r.test()
和str.match()
以及str.replace
等方法。
正则表达式自己有一个test的方法,这个方法只能测试是否包含,返回一个bool变量。
var r = /\d{3}/;
var a = '123';
var b = '123ABC';
var c = 'abc';
r.test(a) //true
r.test(b) //true
r.test(c) //false
复制代码
嗯,这个很简单,并且用的实际很少,下面着重讲str上的一些方法。
与test()不一样,不仅是返回bool变量,它会返回你所匹配到的内容。
var r = /compus/
var reg = /w+/
var s = "compus, I know something about you"
r.test(s) //true
s.match(r) //["compus"]
s.match(reg) //["compus"]
复制代码
等等,好像有点问题,为何最后一个返回的也是"compus"?这不科学。
好吧,实际上,match()返回了第一个能够匹配的序列。想要实现以前的效果,就要用到JS里关于正则的几个flag
这个标志就在创建正则的时候就要有的,主要有三个
flag | 含义 |
---|---|
g | 所有的,给我匹配所有的 |
i | 忽略大小写 |
m | 多行匹配 |
因此为了解决刚才的问题,只要这样子设置reg就能够了
var reg = /w+/g
复制代码
看下面一个练习
var str = "Here is a Phone Number 111-2313 and 133-2311"
var r = /\d{3}[-.]\d{4}/
var rg = /\d{3}[-.]d{4}/g
console.log(str.match(r)); //["111-2313"]
console.log(str.match(rg));//["111-2313","133-2311"]
复制代码
嗯,找电话号码,是的,很方便。可是还有一个问题,刚才说的分组,那么match会返回分组吗?
var sr = /(\d{3})[-.]\d{4}/
var srg = /(\d{3})[-.]\d{4}/g
console.log(str.match(sr)); //["111-2313","111"]
console.log(str.match(srg)); //["111-2313","133-2311"]
复制代码
因此结论是: 当使用了全局flagg
的时候,不会返回分组,而是所有的匹配结果;若是没有使用g
,会将匹配到的结果和分组以数组的形式返回。
那么如何实现全局的分组?
从字面意思来看,正则表达式的执行方法。 这个方法能够实现匹配全局,并返回分组的结果。
reg.exec()每次调用,返回一个匹配的结果,匹配结果和分组以数组的形式返回,不断的调用便可返回下一个结果,直到返回
null
var str = "Here is a Phone Number 111-2313 and 133-2311" ;
var srg = /(\d{3})[-.]\d{4}/g;
var result = srg.exec(str);
while(result !== null) {
console.log(result);
result = srg.exec(str);
}
复制代码
result包含的内容可能比想象中的多,它是一个数组,好比第一次执行,他的结果为:
["133-2311", "133", index: 36,
input: "Here is a Phone Number 111-2313 and 133-2311" groups: undefined]
复制代码
如今来到了更强的功能上,先说下split,咱们知道split是将字符串按照某个字符分隔开,好比有如下一段话,须要将其分割成单词。
var s = "unicorns and rainbows And, Cupcakes"
复制代码
分割成单词,首先想到的是空格隔开,因而能够用下面方式实现
var result = s.split(' ');
var result1 = s.split(/\s/);
//彻底同样的效果
//["unicorns", "and", "rainbows", "And,", "Cupcakes"]
复制代码
嗯,这样体现不出来正则的强大,并且最主要的是没有实现要求。由于还有一个"And,"。因此要用正则了,匹配条件是逗号或者空格
result = s.split(/[,\s]/);
//["unicorns", "and", "rainbows", "And", "", "Cupcakes"]
复制代码
结果仍然和须要的有出入,由于多了一个""。 咱们并非想让它分割的依据是逗号或者空格
,依据应该是逗号或空格所在的连续序列
。 在原来的基础上加一个+
,改为/[,\s]+/
,这个含义就是一个单独的逗号,或者一个单独的空格
result = s.split(/[,\s]+/);
// ["unicorns", "and", "rainbows", "And", "Cupcakes"]
复制代码
好了,拓展一下,实现一个段落的单词分割,一个正则表达式就是
result = s.split(/[,.!?\s]+/)
复制代码
固然,有个最简单的方法,咱们能够这样去作
result = s.split(/\W+/);
复制代码
接着,若是咱们想将一个段落的句子都分隔开,一个能够实现的表达式就是
result = s.split(/[.,!?]+/)
复制代码
最后,有一个小需求,就是在分割句子的同时,还想把相应的分隔符保留下来。
var s =
"Hello,My name is Vincent. Nice to Meet you!What's your name? Haha."
复制代码
这是一个小小的ponit,记住若是想要保留分隔符,只要给匹配的内容分组便可
var result = s.split(/([.,!?]+)/)
//["Hello", ",", "My name is Vincent", ".", " Nice to Meet you", "!", "What's your name", "?", " Haha", ".", ""]
复制代码
能够看到,这样就会把分隔符也存储起来。
replace也是字符串的方法,它的基本用法是str.replace(reg,replace|function)
,第一个参数是正则表达式,表明匹配的内容,第二个参数是替换的字符串或者一个回掉函数。
注意,replace不会修改原字符串,只是返回一个修改后的字符串;除此外,正则表达式若是没有使用g
标志,也和match
同样,只匹配/替换第一个
替换一个序列中的元音字母(aeiou),将其替换成一个double。 好比x->xx
var s = "Hello,My name is Vincent."
var result = s.replace(/([aeiou])/g,"$1$1")
//"Heelloo,My naamee iis Viinceent."
复制代码
注意,第二个参数必须是字符串; 注意不要忘记加g
嗯,这才是最强大的地方,第二参数传入function,先看一个最简单的示例
var s = "Hello,My name is Vincent. What is your name?"
var newStr = s.replace(/\b\w{4}\b/g,replacer)
console.log(newStr)
function replacer(match) {
console.log(match);
return match.toUpperCase();
}
/* name What your name Hello,My NAME is Vincent. WHAT is YOUR NAME? */
复制代码
因此,函数的参数是匹配到的内容,返回的是须要替换的内容。好了,基本示例解释了基本用法,那么以前讨论的分组怎么办?如何实现分组呢?
//分组
function replacer(match,group1,group2) {
console.log(group1);
console.log(group2);
}
复制代码
若是正则表达式分组处理,那么在回调函数中,函数的第二个、第三参数就是group1,group2。这样子,就能够作不少神奇的事情
var s = 'aaabbbcccaaabbbaaa';
var a = s.split('').sort().join(""); //"aaaaaaaaabbbbbbccc"
var ans = a.match(/(\w)\1+/g);
ans.sort(function(a,b) {
return a.length - b.length;
})
console.log('ans is : ' + ans[ans.length-1])
复制代码
/reg/
和字符串字面量"str"
用于建立正则和字符串。其中正则上有两个方法reg.test()
和reg.exec()
reg.test(str)
方法,返回布尔变量,用于指示是否有所匹配; reg.exec(str)
有点相似与迭代器,每次执行,返回匹配结果和分组,直到返回为null
结束。str.match(reg)
,str.split(reg)
和str.replace(reg,str|function)
三种方法。match
比较特殊,若是正则包含了分组,且没有g
标志,则返回匹配内容和分组; 若是没有分组,且有g
标志,返回全部匹配内容split
方法主要用于字符串分割,若是想要保存分隔符,记得将匹配内容分组(用小括号包起来)replace
是最强大的方法,当使用回掉函数时,返回值就是替换值; 参数分别为匹配值
group1
group2
...