乐博娱乐»JavaScript»网上JS正则基础教程没有涉及的一些知识

网上JS正则基础教程没有涉及的一些知识

来源:凉凉凉凉 宣布时间:2018-08-05 阅读次数:乐博

 正则起源

  最近看完了 《精通正则表达式》,收获颇丰,略过了一些艰涩难懂的理论部门,主要看了实战和教程部门。

  下面引用一下百度百科里的内容。

正则表达式的“鼻祖”或许可一直追溯到科学家对人类神经系统事情原理的早期研究。美国新泽西州的Warren McCulloch和出生在美国底特律的Walter Pitts这两位神经生理方面的科学家,研究出了一种用数学方式来描述神经网络的新要领,他们缔造性地将神经系统中的神经元描述成了小而简朴的自动控制元,从而作出了一项伟大的事情革新。

  那么写正则是不是就是把自己神经事情历程通过正则体现出来呢? 好比让小孩子在一堆图形中找到匹配的图形放入凹槽。

乐博

 正则引擎

  正则分几种引擎也从是本书获得的知识点之一。

  • DFA
  • 传统型NFA
  • POSIX NFA

  NFA规模更广,例如 JAVA, PHP, Ruby, .NET... 你是看不起我javascript所以才不列入的吗?

  使用DFA的是flex, MySQL, lex, awk大部门版本… 实话说,除了mysql,都没听过。不外不用在意!

  两个引擎的区别。

  • NFA 更注重表达式
  • DFA 文本主导

  通过书中里例子说,NFA 用表达式来匹配文本,而 DFA 是文原来匹配文表达式。当写好一个正则之后,NFA 是先检查表达式,同时检查文本是否匹配这个表达式。而 DFA 则是先扫描文本,然后处置惩罚表达式中的所有匹配可能,如果匹配失败,就将这条可能的线,淘汰。所以这里衍生一个看法就是回溯,NFA 有回溯,而 DFA 没有。

 知识点

  作为一个菜鸟,正则表达式一直是书到用时方恨少的角色。平时都是能抄则抄,不能抄的时候干着急,只能用 substr, indexOf, chatAt等等的要领实现功效,既不优雅也不够装逼。上网学习也都是菜鸟教程,W3school。然后下面说一下以上两个基础教程里没说到的知识点。

  括号捕捉与反向引用

  当你在正则表达式里使用了 (),在表达式匹配时,它能记着或者说缓存括号内匹配的结果,从而可以拿到括号内的结果,可以重复使用或者只需要括号内的结果,来剔除不需要的匹配内容。

// 我们经常会用 match 要领来匹配字符串,结果是一个数组,而不是最后的匹配结果,为什么呢?看下面的例子
"abc".match(/(a)(b)(c)/) // ["abc", "a", "b", "c"]
"abc".match(/abc/) // ["abc"]

  可以看到,括号会缓存括号里匹配的内容,单独列出来,那么怎么拿到括号内的内容呢,而不是通过 match 返回的结果拿,因为有时候我们需要在表达式里使用捕捉的值,从而到达匹配重复的内容。这部门就叫反向引用。

"abc-abc-cba".replace(/(a)(b)c-\1\2/, '') // c-cba
"abc-abc-cba".replace(/(a)(b)c/g, '$1$2') // ab-ab-cba
RegExp.$1 // a
RegExp.$2 // b

  这里展示了两种使用反向引用的要领,一种是在表达式内通过 \1\2 的形式拿到两个缓存的值,一种是使用 $1$2的形式拿到。因为正则是从左开始匹配的,所以 (a) 就是第一个捕捉的匹配值,所以他是\1 或是 $1,以此类推。

  非捕捉型括号

  上面说了括号会捕捉值,一般来说这样会影响性能,或者你会用到括号来做分组,但是不想捕捉的情况,(?:)非捕捉型括号就是这么用的,那么重写一下上面的例子。

"abc-abc-cba".replace(/(a)(?:b)c-\1\2/, '') // 匹配失败了,因为\2不存在
"abc-abc-cba".replace(/(a)(?:b)c-\1/, '') // bc-cba
RegExp.$1 // a
RegExp.$2 // ""

  环视

 

类型 正则表达式
肯定逆序环视 ?<=
否认逆序环视 ?<!
肯定顺序环视 ?=
否认顺序环视 ?!

  ?= 和 ?! 在菜鸟和w3school 里有简朴的提及,菜鸟里还提到这两个还能重写捕捉,但是 ?<= 和 ?<! 并没有提及。

  写几个 demo 体现一下:

// 找一个字母 a ,它紧跟在 b 前面
"abac".replace(/a(?=b)/g, '') // bac

// 找到一个字母 a ,它紧跟在一个不是 b 的字母前面
"abac".replace(/a(?!b)/g, '') // abc

// 接着是逆序环视
// 找到一个字母 a ,它跟在 b 后面
"abac".replace(/(?<=b)a/g, '') // abc

// 找到一个字母 a ,他不跟在 b 后面
"abac".replace(/(?<!b)a/g, '') // bac

// 一个有趣匹配
// 在 a 和 b 之间插入一个 ","
"abac".replace(/(?<=a)(?=b)/g, ",") // a,bac

  可以看出,环视是要和捕捉括号一起用的,而且不会占用匹配字符,他只是检查表达式是否匹配。所以这就是重写捕捉了。

  忽略优先量词

  量词匹配一般有三种 *、 +、?。然而还可以写作, *? 或 +? ,使匹配结果导向完全差异的结果。例子:

"abc-aaa-abc-abc".replace(/abc-.*-abc/, '') // ""

"abc-aaa-abc-abc".replace(/abc-.*?-abc/, '') // "-abc"

  *? 忽略优先会先忽略当前匹配的值,先匹配后面的 -abc,如果匹配失败,再匹配自己,而 *会优先匹配自己,等匹配结束之后,再从后面一点点吐出,回来匹配量词后面的表达式。从而造成以上差异的结果。知道这个之后,就不会再傻傻的把 * 和 ? 离开解读了。虽然,具体情况具体分析,到底使用哪个。

  回溯

  回溯应该算是正则里的性能杀手了吧。如果表达式写的欠好,造成太过的灾难性回溯,会导致执行时间指数级增长。具体情况照旧通过搜索引擎了解吧,解释起来会过长,而且作为正则新手的我还纷歧定能解释清楚。。。

 最后

  以上是我在《精通正则表达式》一书中获得的一些收获,希望能分享给各人,如有错误接待指正。下一步呢就是去做一些练习来牢固一下了。