正则表达式之位置匹配

前端之家收集整理的这篇文章主要介绍了正则表达式之位置匹配前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

Sharplook夏洛克作为专业的日志采集分析系统,涉及的技术点,从后到前着实不少,内容也较为复杂。正则作为日志解析的手段,起着举足轻重的作用,在此小生将晦涩难懂的内容,拆解出来以便学习之用。

往期回顾

上一篇文章中,我们已经学习了正则表达式的字符匹配,介绍了模糊匹配、字符组和量词。正则表达式就是匹配模式,要么匹配字符,要么匹配位置,牢记这个基准。本章小生将介绍位置匹配,将分成以下内容进行介绍:

  • 位置的定义
  • 位置如何匹配
  • 位置的特性

位置的定义

位置可以理解为字符之间的位置,也可以称为锚点。如图,箭头所指的位置:

如何匹配位置

在ES5中,共有6个锚:^$\b\B(?=p)(?!p)

^$\b\B(?=a)(?!b) 其可视化的形式如下:其可视化的形式如下:

^$

^(脱字符)匹配开头的位置,在多行匹配中匹配行开头。

$(美元符)匹配结尾的位置,在多行匹配中匹配行结尾。

比如我们把字符串的开头和结尾替换为#,就是将位置替换为字符:

let result = 'lemon'.replace(/^|$/g,'#');
console.log(result);
// => #lemon#

多行匹配模式(增加修饰符 m)时,此是行的概念,这一点需要我们注意:

let result = 'I\nlove\njavascript'.replace(/$|^/gm,'#');
console.log(result);
/*
#I#
#love#
#javascript#
*/

\b\B

\b 是单词边界符,具体为\w\W之间的位置,也包括\w^之间的位置,和\w$之间的位置。

比如,我们通过字符串替换字符串中的\b位置为#来进行分析:

let result = '[JS] Lesson_01.mp4'.replace(/\b/g,'#');
console.log(result);
// => [#JS#] #Lesson_01#.#mp4#

具体怎么匹配成这样的? 接下来我们慢慢来分析。

首先我们需要知道,\w 是字符组 [a-zA-Z0-9_]的简写,也就是\w是字母、数字和下划线中的任何一个。而 \W 是排除字符组 [^a-zA-Z0-9_]的简写形式,即\W\w以外的任何一个字符。

我们再来看我们的结果[#JS#] #Lesson_01#.#mp4#中的每一个#的由来:

  • 第一个,两边字符是 [J,是 \W\w 之间的位置。
  • 第二个,两边字符是 S],是 \w\W 之间的位置。
  • 第三个,两边字符是空格与 L,是 \W \w 之间的位置。
  • 第四个,两边字符是 1.,是 \w\W 之间的位置。
  • 第五个,两边字符是 .m,是 \W\w之间的位置。
  • 第六个,位于结尾,前面的字符 4\w,即 \w$ 之间的位置。

知道了 \b 的概念之后,接下来 \B 就很好理解了。\B 就是 \b 的反面意思,非单词边界,例如上例子中除去 \b 的位置之外,余下的就都是 \B 的位置了。具体来说就是 \w\w\W\W^\W\W$ 之间的位置。

比如,上例子中,把所有 \B 替换成 #:

let result = '[JS] Lesson_01.mp4'.replace(/\B/g,'#');
console.log(result);
// => #[J#S]# L#e#s#s#o#n#_#0#1.m#p#4

(?=p)(?!p)

(?=p),其中 p是一个子模式,即 p 前面的位置,或者说,该位置后面的字符要匹配 p。比如(?=l),表示 l 字符前面的位置,例如:

let result = 'lemon'.replace(/(?=l)/g,'#');
console.log(result);
// => #lemon

(?!p) 就是(?=p) 的反面意思,比如:

let result = 'lemon'.replace(/(?!l)/g,'#');
console.log(result);
// => l#e#m#o#n#

二者的学名分别是positive lookahead 和 negative lookahead。中文意思分别为正向先行断言和负向先行断言。ES5之后的版本,会支持positive lookbehind 和 negative lookbehind。意思是正向后行断言和负向后行断言。具体是(?<=p)(?<!p)。例如:

let result = 'lemon'.replace(/(?<=l)/g,'#');
console.log(result);
// => l#emon

表示该位置之前的字符要匹配 l 表达式。而 (?<!p) 为:

let result = 'lemon'.replace(/(?<=l)/g,'#');
console.log(result);
// => #le#m#o#n#

表示该位置之前的字符不能匹配 l

其实 (?=p)^ 一样好理解,将其理解成 p 前面的那个位置即可,其他类似。

位置的特性

对于位置的理解,我们可以将其理解成空字符 ""。 比如lemon 字符串可以写成如下的形式:

'lemon' === '' + 'l' + 'e' + 'm' + 'o' + 'n'+ '';

也可以写成如下:

'lemon' === '' + 'lemon' + '';

因此,把 ^lemon$ 写成 ^^lemon$$$,是没有任何问题的:

let result = /^^lemon$$/.test('lemon');
console.log(result);
// => true

甚至可以写成更复杂的:

let result = /(?=le)^^le(?=\w)mon$\b\b$/.test('lemon');
console.log(result);
// => true

也就是说字符之间的位置,可以写成多个。

把位置理解成空字符,是对位置非常有效的理解方式。

小结

本节介绍了位置的匹配,需要诸君理解 ^$\b\B(?=p)(?!p)(?<=p)(?<!p)中的位置匹配的用法

本文中的正则表达式转化为关系图来展示的工具是Regexper

此文主要参考和学习了老姚的《JavaScript 正则表达式迷你书》,内容清晰明了,在此非常感谢老姚的 free精神,致敬。

参考文献

[1] 老姚 著《JavaScript 正则表达式迷你书》

猜你在找的正则表达式相关文章