正则表达小结与小知识点集锦

背景介绍

这几天,刚来公司,导师安排点任务增加些功能,以便熟悉了开发环境。接到的任务中,有一个环节需要处理一个业务的json数据,具有多级嵌套,我要做的是让使用者可以修改其中的”userName”。

有两个思路

  • 利用正则表达式进行匹配替代
  • 利用遍历修改键值

当然,看到两个思路的高下,在大部分情况下都是可以直接得出结论的,正则肯定是优于遍历的。

正则表达式小结

创建方式

  • 直接量语法
    /pattern/attributes

  • 创建RegEx对象的语法
    new RegExp(pattern,attributes)

语法属性说明:pattern我们写的正则表达式的规则,而attributes则是规则的修饰符,g为全局匹配,i为区分大小写的匹配,m为多行匹配。

语法

括号

用于查找某个范围内的字符

1
2
3
4
5
[acg] //匹配acg任一项即可
[^acg]//匹配acg之外的任何一个字符
[a-z] //匹配a到z 26个小写字母的任何一个即可
[A-Z] //匹配A到Z 26个大写字母的任何一个即可
(red|blue|green) //匹配red或者blue或者green任何一个即可

元字符

拥有特殊含义的字符

1
2
3
4
5
6
. //查找单个字符,除了换行以及行结束符
\w //查找单词字符
\W //查找非单词字符
\d //查找数字
\D //查找非数字
\xxx //查找八进制书xxx规定的字符

量词

描述规则执行的次数要求以及位置要求

1
2
3
4
5
6
7
8
9
10
n+ //匹配任何包含至少一个n的字符串
n? //匹配任何包含零个或者一个n的字符串
n* //匹配包含任一个n的字符串
n{x,y} //匹配包含x到y次n的字符串
^n //匹配开头含有n的字符串
n$ //匹配结尾有n的字符串
?=n //匹配任何其后紧跟指定字符串n的字符串
?!n //匹配任何其后没有紧跟字符串n的字符串
\b //匹配一个字边界,即字与空格间的位置
\B //非字边界匹配

方法

RegExp的方法

  • compile 编译正则表达式
  • exec 执行正则表达式,并返回找到的值与位置
  • test 检测是否真的含有符合正则表达式的字符串,返回布尔值

String对象的方法

  • search 检索与正则表达式相匹配的值
  • match 找到一个或者多个正则表达式的匹配
  • replace 替换与正则表达式相匹配的子串
  • split 把字符串分割为字符串数组

小知识点集锦

子表达式

一个正则表达式可分为许多子表达式
例子:利用子表达式可以将通用资源指示符 (URI) 分解为其组件。假定您想将下面的 URI 分解为协议(ftp、http 等等)、域地址和页/路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var test = http://www.w3cschool.cc:80/html/html-tutorial.html;
/\w+:\/\/[^/:]+:\d*?[^# ]*/.exec(test)
//只可以匹配到链接
//输出结果
["http://www.w3cschool.cc:80/html/html-tutorial.html",
index: 0, input: "http://www.w3cschool.cc:80/html/html-tutorial.html"]
/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/.exec(test)
//不仅可以匹配到链接,还可以把链接分为各个部分输出
//输出结果
["http://www.w3cschool.cc:80/html/html-tutorial.html",
"http", "www.w3cschool.cc", ":80", "/html/html-tutorial.html",
index: 0, input: "http://www.w3cschool.cc:80/html/html-tutorial.html"]

贪婪的量词

? * + 三者都是贪婪的

因为他们会尽可能多的匹配字符串,只要在他们后面再加一个?就可以实现非贪婪或者最小匹配
实例如下

1
2
3
4
5
<h1>Hello world!<h1>
//下面的表达式匹配从 < 到关闭h1标记的 >之间的所有内容
/<.*>/
//如果你只需要匹配开始的h1标记,下面的非贪婪表达式只匹配<h1>
/<.*?>/

圆括号的副作用消除

圆括号有一个副作用,那就是相关匹配会被缓存,此时可在在圆括号中加上?:在写正则表达式

1
/(?:<.*>)/

String.match(/<.+>/g) 与 RegExp.exec(string)的区别

不得不说这个是让我迷惑的很久的坑

  • String.match(/<.+>/g)
    这个是字符串方法,返回数组,包括与正则表达式匹配的第一个或者所有字符串,是否返回多个值由修饰符g决定(不返回子表达式的匹配结果)
  • RegExp.exec(string)
    这个是正则表达式对象的特有方法,返回一个数组。数组包含:正则表达式匹配到的第一个字符串,各个子字符串匹配到的字符串,另外还有两个键值index与input,分别输出匹配到的字符串的第一个字符的位置与被检测的字符串。
    所以想要输出()的子字符串匹配的字符串必须要用exec,(?:)同样也只要在用exec的时候才能检测到效果。

RegExp.exec(string) 总结

介绍

这个可以取到各个子表示的结果,并且可以返回对应的index值。拥有非常大的想象空间。比如我想获
"userName":"test"的值使用match,我们需要先匹配整个键值对,再对这个键值对进行处理才能取到键值。而使用exec,通过子表达式,我们则直接可以获取到键值。

当然,exec有个蛋疼的地方是只能取到第一个匹配的字符串,也就是说修饰符g设置没设置对他来说都一样。

那我们如何用exec来全局匹配呢?

首先,我们来了解一下exec的一个特殊地方

当exec执行全局匹配模式时,exec的行为就略有变化。这时它会定义lastIndex属性,以指定下一次执行匹配时开始检索字符串的位置。在找到了与表达式相匹配的文本之后,exec方法将把正则表达式的lastIndex属性设置为下一次匹配执行的第一个字符的位置。也就是说,可以通过反复地调用exec方法来遍历字符串中的所有匹配文本。当exec再也找不到匹配的文本时,将返回null,并且把属性lastIndex重置为0。

1
2
3
4
5
6
7
8
9
10
var s = "Hello world Hello world"; // 测试使用的字符串直接量
var r = /Hello/g; // 匹配模式 一定要加上修饰符g
while((a = r.exec(s)) != null){ // 循环执行匹配操作
console.log(a);
console.log(r.lastIndex);
/* 显示每次匹配操作时返回的结果数组信息*/
}

测试结果

1
2
3
4
["Hello", index: 0, input: "Hello world Hello world Hello world"]
5
["Hello", index: 12, input: "Hello world Hello world Hello world"]
17

bingo,完成全局匹配。注意正则表达式,一定要加上修饰符g。要不然lastindex并不会改变,
循环会始终为真,不断执行。

结语

这篇小结,也是花了好几个小时去查资料,测试最后写出来的,之前也看过许多正则的资料,却始终停留在看看,认为知道了,每次想起来的时候,又都忘了,前几天,用的时候就手忙脚乱的边查资料边coding。终于认识到了自己的学习方式给自己带来的深刻问题。

其实,走上技术的路,时间也不短了。
但确实在技术学习的路上,有许多观点,随着时间与自身情况的变化,并未及时的更新。

曾经,在决定未来走向的时候,为了海量的获取信息来看到更远的未来,从而找寻一条能够坚定走下路。读书与阅读的时候,采用快速阅读的方式,不去细细咀嚼,只觉得看过一遍,迅速了解扩展视野,了解更多的东西就好。开始的时候,这种方式为我建立了认知,获得了远大于自身水平的视野。但是视野不曾有技术的支撑,只是一幢危楼而已。行之今日,这种学习方式暴露的问题,越来越大。后来,虽有意识,却不曾有动力予以改正。

幸得近来被人指正,得以真正意识到问题的严重性。

非自己所研究的方向,有一定认识就好,用不着过度深入,采用快速阅读的方式,自然是极好的。能够快速建立认识。但是真正自己的研究方向,发散性阅读才是更好的方式,对于每个点,都不仅仅局限于理解这一个点,能够另行查阅许多相关的资料,这样的方式,看一本书读懂一本书,并扩展阅读了许多相关的资料。这确实是更为行之有效的技术学习方式。而不像之前的方式,看似读过,却处处有漏洞,不曾真正的读透一本书,希望临到项目,临到业务。再去真正实践,去真正的掌握。是一种极为不靠谱的方式。

学习的方法的重要性,不亚于学习本身,也是值得我们去反思并改进的。

参考资料

正则表达式|菜鸟教程
W3Cschool正则表达式
使用exec增强正则表达式功能

分享 留言