9

复习正则表达式

最近研究ES6, 正好ES6也有正则方面的内容, 看看有什么新特性, 顺便又把精通正则表达式拿出来粗看了前面几章节, 于是给自己出了几道题. 还做了一点总结.

[\b]\b\B

一般作为初学者看到这么多这些鬼都会头大了. 下面我将详细讲解一下.

[\b] - 退格符

另外其实我一直都搞不清楚[\b]匹配一个退格(U+0008)是什么鬼. 似乎没有人告诉我这个退格符长什么样. 我也不知道那些各种转载各种规则的人他们自己知不知道是啥... (难道就我不知道- -)

找了半天, 总算在MSDN: 正则表达式语言 - 快速参考找到了示例. 难道就是匹配\b用的吗?, 当然很明显的区别是, 它属于字符转义

\b - 一个词的边界

MSDN: 匹配必须出现在 \w(字母数字)和 \W(非字母数字)字符之间的边界上。

这个就很好理解了, 会写先行断言的我当然是知道了, 他不占用任何位置, 边界一般都是单词或数字两边, 更为具体的通过MDN的正则表达式文档\b介绍内的注意有指引, 查到ecma文档的15.10.2.6 AssertionIsWordChar处, 不过由于个人能力有限, 对其理解如下:

通过这段代码(正则表达式案例分析 (一) - (3) 单词边界):

"I'd prefer p2p O_O".replace(/\b/g,function(){
  console.log(arguments)
});

输出结果:

{ '0': '', '1': 0, '2': 'I\'d prefer p2p O_O' }
{ '0': '', '1': 1, '2': 'I\'d prefer p2p O_O' }
{ '0': '', '1': 2, '2': 'I\'d prefer p2p O_O' }
{ '0': '', '1': 3, '2': 'I\'d prefer p2p O_O' }
{ '0': '', '1': 4, '2': 'I\'d prefer p2p O_O' }
{ '0': '', '1': 10, '2': 'I\'d prefer p2p O_O' }
{ '0': '', '1': 11, '2': 'I\'d prefer p2p O_O' }
{ '0': '', '1': 14, '2': 'I\'d prefer p2p O_O' }
{ '0': '', '1': 15, '2': 'I\'d prefer p2p O_O' }
{ '0': '', '1': 18, '2': 'I\'d prefer p2p O_O' }

我们看到被断掉(用|表示)的位置分别是:

图示定位符b

也就是说连续的单词数字_组合(上文提到的ecma部分的表格也对应了这个)都是一个单位, 他的两侧就是截断. 除此之外的任何符号都会截断他们.

另外, 在MSDN文档中, 它被归为定位点.

\B - 一个非单词边界

前面说了这么多, \B的理解就很轻松了, 一个非单词边界. 就不多说了, 看看MSDN的例子就清楚了.

模式: \Bend\w*\b

原字符串: end sends endure lender

匹配结果: endsender

关于正则个人的经验

其实搞正则匹配, 我个人的从精通正则表达式书中阅读后的感受就是, 匹配一定要一个个看, 慢慢的看, 比如上面这个例子, 我看看模式先是\B, 然后再找原字符串, 依次步骤分析:

  1. 第一个e左边(位置)是边界不符合, 失败看下一个字符
  2. 第二个n左边(位置)符合\B, 匹配成功, 再看模式\B后面的e
  3. 模式\B(位置)后面的e不匹配n, 失败再看下一个字符d
  4. 第三个d左边(位置)符合\B, 匹配成功, 再看模式\B后面的e
  5. 模式\B(位置)后面的e不匹配第三个字母d, 失败再看下一个字符 (这里是个空格啦?)
  6. ...(此处省略, 一直到send单词)
  7. (前面都不符合,当遇到了send), 经过一步步后移, 模式\B(位置)走到了s右侧, 成功! 此刻, 兴奋的将模式移到第二个e
  8. 好巧, 模式中的e匹配到s后面的字符e, 再回到模式下一位n
  9. 世界太小了, 又一次成功了, 紧接着是d, 看起来一一对应上了
  10. ...(截至目前, \Bend部分已经和sends中的end配上了, 可是还没完呢)
  11. 模式d后面是\w*, 我们回到原字符串部分send后面是s所以也成功
  12. 再看模式部分\w*下一位\b, 碰到上面讲的单词边界了( •̀ ω •́ )y, 我们看看原字符串部分sends 这里的确被截断了, 因此原字符串开始新一轮匹配
  13. ...(反复如上步骤)

最后就得出了匹配结果的两组字符串了. 不知道我这样讲大家能不能理解, 或者说这种思路大家有没有疑问和反对之处, 如果有希望大家留言?

两道正则题目

自己折磨自己

题一: 找出符合规则的时间

匹配符合标准格式的时间. 这是内容部分:

now is 09:4 am test
now isx9:4 am test
now isx2:54 am test
now isA09:04 amwtest
now is_12:30 pm adsadadasda
now is 21:59 amdsadasdwq
now is 22:75 am_dsad21
now is 41:60 pm   dsadsad 
now is 26:23 am   dwadwq
now is 2a:23 am   dwadwq

期望结果(虽然24小时制后面存在[ap]m感觉还是不太合理.)

09:4 am
9:4 am
2:54 am
09:04 am
12:30 pm
21:59 am

看起来应该不是很复杂. 结果我写了一天没写出来. 因为我不知道怎么剔除26. 似乎无论怎么写, 在26之间正向断言始终都会匹配到6. ES5却是不支持后行断言(negative lookbehind)的, 据说ES6支持后行断言了, 才得以解决这个问题, 然而不通过后行断言来处理这个问题, 我始终没有写出来, 难道真的是实现不了?

这是一个未能完美解决的其中一种写法.

/([01]\d|(?=2(?![4-9]))2\d?|(?!2)\d):(0?(?=\d)[0-9]|[1-5][0-9])(\s*[a|p]m)/gim

这是通过后行断言的写法:

/([01]\d:|(?=2(?![4-9]))2\d?:|(?<!2)\d:)(0?(?=\d)[0-9]|[1-5][0-9])(\s*[a|p]m)/gim

题二: 找出文件后缀名

再来看另一个, 获取文件后缀名的正则写法问题:

可能有以下文件, 需要准确获取每个文件的文件类型, 例如:

a.jpg
hello world.png
c.c.mp3
_do_(it)._unknow

期望:

.jpg
.png
.mp3
._unknow

这个其实还算按比较简单的了. 不过在我复习正则之前硬是想不起来咋写, 稍微过了一遍再写就容易多了. 正则写法\..(?!\.).*.

改题目来源于前几天同事发的一个javascript自验网站: ScriptOJ首页的题目.

关于题目部分, 不知道大家也有没有更好的写法, 也欢迎大家探讨纠正哦?

whidy
984 声望79 粉丝

喜欢玩游戏听歌写东西 ฅʕ•̫͡•ʔฅ