首页
社区
课程
招聘
最实用的RegExp(正则表达式)技巧
2018-5-31 21:41 3852

最实用的RegExp(正则表达式)技巧

2018-5-31 21:41
3852

最实用的RegExp(正则表达式)技巧

我并不经常使用RegExp,一旦需要,我会使用多种方法。

注意:此博客文章使用JavaScript作为示例,但并不是只有JavaScript才可以使用这些技巧。

我们时常需要从一大段文本中提取出带引号的字符串、HTML标签或是大括号之间的内容。尽管写一些适当的分析程序会使代码更加健壮、更易于维护和可读,但我们经常选择正则表达式(或简称RegExp),因为你只需要搜索现成的RegExp并使用它就行。你在网络上找到的基于RegExp的解决方案都不是最优的,并且很难读懂它们的工作原理。

简便方法

让我们来看一下提取字符串的例子;你面临的情况可能是,需要从一段 JavaScript中,提取出带引号的字符串:

输入:console.log("Hello world!"); console.log("Hello back!");
理想的匹配:['"Hello world!"', '"Hello back!"']

刚接触到RegExp的人可能会想是/".*"/gus完成了提取工作,让我们来仔细分析这个RegExp的含义:

/…/gus ——在JavaScript(以及许多其他语言)中RegExp以斜线分隔,后跟模式标志。g表示在同一个字符串中可能有多个匹配,我们对它们都感兴趣。u能够将模式视为Unicode,这通常来说更有意义;s允许.匹配\n。

" ——匹配一个"字符

. ——匹配任何字符

* ——重复最后一次操作符0次或多次

" ——再一次匹配一个"字符


注意:在JavaScript中,s和u都是相当新的标志,可能不是所有的浏览器都会支持。


在我们的输入字符串中运行这个RegExp会产生一个意想不到的结果: "Hello world!"); console.log("Hello back!" 这就是计算机“在技术上是正确的”的情况——这个字符串两端确实有引号,并且在这之间确有一段的任意字符,但并没有达到我们想要的效果。

我最经常看到的解决方案是人们切换到“non-greedy”模式*:

/".*?"/gus

这里的RegExp和上面那个相似,但是它却让*通配符尽量少的去匹配字符(即non-greedy,非贪婪模式),以此来得到我们期望的结果.


就个人而言,我对非贪婪模式的匹配存在信任问题,更关键的是:在console.log("Hello \"world\"!");上运行正则表达式会发生什么呢?


显然没有达到效果。

诀窍

反斜杠背叛了我们!所以现在怎么办?这就是我即将要说的诀窍了,我将我的RegExp告诉你,而且会向你展示我是如何做到的:/"([^"\\]|\\.)*"/gus

没错,我们只是用了RegExp中的一个,漂亮不?

第一个问题是,虽然/".*?"/gus能够起一点作用,但还是没能达到我们的目的。我们并不想得到双引号间的任何字符,只需要双引号。如何做到这一点呢?在RegExps中,你可以使用字符组来匹配整个字符集:

[abc]——匹配所包含的任意一个字符,如a,b,c;

[a-z]——匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符;

[^abc]——匹配除a,b,c外的任何字符;

考虑到这一点,我们可以在没有非贪婪匹配的情况下编写我们的原始RegExp:

/"[^"]*"/gus

但是,这仍然不能解决反斜杠问题。为此,我们需要强调上面提到的问题:我们想要的是双引号而并非双引号间的任何字符,但如果是一个反斜线,我们将忽略其后的字符。 这让我们回到了神秘的原始RegExp(为分组添加了一些空间):

/"   (   [^"\\]   |   \\.   )*   "/gus

(...|...) ——匹配列出的选项中的任意一个,用 | 分界(此项中只有两个选项);

[^"\]——匹配除双引号和反斜线之外的任意字符;

\. ——匹配一个反斜线和其后的任何单字符

这里的诀窍是提供两个互斥的选择方案。第一种方案不能匹配双引号。如果一个带有双引号的字符串应当匹配这个RegExp,那么第二个选择必须是与之匹配的字符串,只有在前面加上反斜杠时才会发生这种情况。

你看,这个RegExp甚至可以匹配字符串中溢出的引号!


真的是酷毙了!

奖励:HTML标签

HTML标签是非常有趣的,因为它的(无限数量的)闭合字符">"在转义之后并不包含原字符">"(而是类似>)。因此,在大多数情况下,正则表达式“/<[^>]*>/”可以完成匹配标签的作用。

直到你在属性值中使用">"字符!

输入<a href="javascript:alert('>');" target="_blank">lol</a>
理想输出: <a href="javascript:alert('>');" target="_blank">

为了处理这种情况,我们必须两次使用我们的新技巧。结束标记“ >”出现在HTML标记中的唯一方法是包含在一个字符串中。因此我们的第一个选项将接受任除结束标签和双引号外的任何内容。第二个是我们之前的字符串 RegExp:

/<([^>"]| string RegExp )*>/gus

或者将它写完整:

/<([^>"]|"([^"\\]|\\.)*")*>/gus

很难懂吧?但它确实起作用了!


部分建议

这是 Jamie Zawinski的名言为何这么出名的原因之一:

有些人在面对难题时,会想“我将使用正则表达式来解决它”,之后他们就有了两个难题。

RegExp往往很快就能造成难以维系的混乱,因此我们应当谨慎的使用它。请记住,大多数语言的语法都不能使用RegExp进行分析。但最重要的是:最好不要自作聪明,而应该用简单的方法调用来进行字符串操作。


翻译:看雪翻译小组 Logdty

校对:看雪翻译小组 StrokMitream


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2019-1-26 14:55 被admin编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回