如何教你看懂复杂的正则表达式[转载]

本文最后更新于:3 years ago

原文链接:https://www.cnblogs.com/superstar/p/6638970.html

好久没更新了,是因为这几天都在学习python,渗透的学习也拖了一点。为什么老转载呢,这是个好问题
一、我所知道的知识还是太少了,总结的其实很不全面;
二、一些大神写得太具体了,这里转载一下,以后可以复习一下。
正则表达式我相信是每个渗透学习者入门所必须要学习的知识,并且也是一道很高的墙,有的正则表达式想要绕过去
还是很难的,这里转载了一篇大神所写的一篇文章,很好的描述了如何看懂复杂的正则表达式这里放上原文地址
如何教你看懂复杂的正则表达式

【前言】

1.此文针对,正则表达式的初学者,老鸟请飘过。

正则表达式的初学者,常遇到的情况是,对于相对复杂一点的正则表达式,觉得很难理解,很难看懂。

2.此文目的,之前你看不懂,看了此教程后,就基本掌握了,看懂复杂正则表达式的思路。

这样就可以通过自己的能力,一点点把复杂的正则表达式,一点点拆分,一点点分析,知道完全理解。

3.在看此文之前,肯定需要你本身对于正则表达式,已经有了一些基本的基础,

比如知道点’.’表示任意字符,星号’*’表示0或多个之类的含义,这样才有看此文的基础。

 

关于正则表达式方面的教程和资料,需要的可以去看我整理的一些资料:

正则表达式学习心得

【教程】详解Python正则表达式

【总结】关于(C#和Python中的)正则表达式

java中的正则表达式:java.util.regex

 

【如何看懂复杂的正则表达式】

基本思路:拆分->各个击破

解释:

先将一个,很长的,很复杂的正则表达式,从左向右,一点点读取,分析,一点找到某部分的内容,是一个逻辑概念上的独立的一块,就暂时拆分出来,如此,一点点把复杂的正则表达式,拆分成很多个逻辑上独立的小块,

然后针对每个小块的表达式,再去分析其含义

每个小块的正则表达式都搞懂后

把和所有的含义,合并出一个整体的含义

最后就可以实现,用人类的语言,把对应的复杂的正则表达式,一点点解释出来了,即:

把,之前看不懂的,复杂的正则表达式,翻译成,人类可以看懂读懂的语言(至少先让你自己读懂看懂)

 

在举例分析之前,需要知道一些前提:

1.任何复杂的正则表达式,都是由写正则表达式的人,从简单到复杂一点点写出来的。

所以,理论上,如何读懂复杂的正则表达式,也就是一个反向解析的过程,即将复杂的拆分成多个简单的,功能上,逻辑是独立的子表达式,然后再去分析其含义,最终再合并出来整体的,复杂的含义。

2.正则表达式,即使各种语言的正则表达式的库函数,去解析的时候,也是从左到右,一点点分析,一点点拆分,将复杂的差分成多个子表达式,以实现,计算机语言内部,去理解此表达式的。

此处,只是通过人类的方式,手动从左到右,一点点分析而已,也算是和计算机语言识别正则的类似的过程。

3.虽然正则表达式,不同的语言,具体的写法,有些略微的差别,但是本质上的,绝大部分的正则表达式的写法,都是基本类似的。

 

【举例说明,如何实现拆分复杂的正则表达式】

举例:

/^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$/i正则表达式表示什么意思?

首先,对于拿到这个,看似很繁琐的字符串:

/^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$/i

作为,相对比你熟悉正则的我,一看就知道,是PHP或Perl一类的语言中的正则表达式,因为这里是:

/xxx/i

的格式,其中xxx表示真正的正则表达式本身,而后面的i表示ignoreCase,即忽略大小写的意思。

而如果你只是熟悉其他如Python等语言的正则表达式,则此处无需太关心那两个斜杠,可以将其理解为,类似于Python中的这样的写法:

re.match("xxx", re.I)

其中的xxx,是此处真正的正则表达式:

^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$

而re.I即re.IGNORECASE,表示忽略大小写的意思。

 

接下来,就来分析此处的xxx,即:

^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$

的完整的含义:

对于我来说,看到此正则表达式:

^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$

后,我可以直接将其,按照之前所介绍的方法,直接拆分出对应,几个子表达式,其中每个子表达式,相对来说,是逻辑上独立的,或者是没关系,关系不大的各个小的正则表达式。

先说拆分的结果如下:

1 ^
2 [A-Z]
3 :
4 \\{1,2}
5 [^/:\*\?<>\|]+
6 \.
7 (jpg|gif|png|bmp)
8 $

 

但是,作为读者的你,肯定看了会说,我怎么才能,像我这里一样,一次性就看出,如何将上述复杂的正则表达式,一下次分出这8部分,即(将上面那个复杂的正则表达式)大卸八块呢?^_^

那么此处,就来介绍一下,基本的思路,或者说,我是怎么实现此过程的:

【如何拆分正则表达式: ^[A-Z]:\\{1,2}[^/:\*\?<>\|]+\.(jpg|gif|png|bmp)$】

首先,看到这么一堆的复杂的字符,其实我也不可能立刻实现,全部拆分出来。

我也是一点点,像之前介绍的方法和思路一样,是从左到右,一点点去,识别,区分,然后一点点分出来这么多个子表达式,子部分的:

 

1.比如,首先,我从左往右看的话,第一个看到的是’^’,对此,对于有了正则表达式最最基础的你,应该知道,这个是匹配字符串的开始的;

而很明显,对于’^’,此处,一般不会,此处也没有后面有啥限定符,即没有和其他字符,去搭配使用。

所以,此’^’,就是我们所拆分出来的,第一个,相对逻辑上独立的,子正则表达式,所以就可以写出第一个小子表达式了:

1 ^

 

2.然后接着来分析,接下来是左中括号'[‘,而对于左中括号,还是那句话,作为已有正则表达式的基础的你,知道其,一般来说都是和另外一个右中括号’]’去搭配使用,并且左右中括号里面,也会有一些字符,以表示中括号内的字符,所组成的集合,即类似于[xxx]的形式,对此,接着往后看,可以说,此处还是很简单的,就看到了后面还有’A-Z]’,正好和'[‘组成了'[A-Z]’,正好符合我们所理解和期望的[xxx]的形式。

而此处,很明显,就是A-Z,对应着正则表达式的语法,在中括号内,可以通过短横线链接起始字符,表示一段范围内的字符,此处即通过A-Z表示,A,B,C,。。。,X,Y,Z,这26个大写字母。

所以,此处,看似,也就很清楚了,觉得第二个子正则表达式,就是[A-Z]了。

而作为比你经验稍多的我,要告诉你,其实你此处这样的想法,是严谨的,因为,对于,中括号内部表示字符集合,[xxx]的写法,往往后面还会跟着一些限定符,去表示此集合字符的个数方面的限定,比如加号’+’表示去匹配,往后数,尽可能多个,比如表示最少2个,最多5个的'{2,5}’等等。

而此处呢,算是巧了,后面实际上,是没有这类限定符的,因为我们看到了,后面只有冒号’:’,而冒号,此处,正如按照正常逻辑所理解的一样,就是表示匹配冒号字符’:’本身而已。

所以,此处,由于巧了,后面没有字符个数方面的限定符,所以,第二个子正则表达式,正好就是[A-Z]本身而已,所以,接着写出,我们已经拆分出来的,共两个子正则表达式了:

1 ^
2 [A-Z]

 

3.上面已经分析了,此处后面跟着的字符,是冒号这个字符’:’,同理,由于后面没有看到其他的加号’+’之类的限定符,所以此处,冒号本身,就是表示一个完整的子正则表达式,去匹配单个的冒号了。

所以,此处第三个,子正则表达式也就是此冒号字符本身了。所以,现在共拆分出来三个子正则表达式了:

1 ^
2 [A-Z]
3 :

 

4.可以看到,冒号后面是个反斜杠’\’,而看到反斜杠,作为已了解正则表达式的语法的你,应该知道,正则表达式中,会有很多’\x’其中x是某个字母的形式,而不同的字符,组合出来的,表示不同的各种含义,比如常见的\d表示数字0-9等等。

而此处,看到的是反斜杠后面’\’后面,又跟了个反斜杠’\’,对此,根据正则表达式的语法,则是表示反斜杠这个字符本身,就是想要去匹配一段字符串中,是否有反斜杠这个字符本身。

然后接着往后看,是{1,2},很明显,是之前已提到多次的,限定符,作用是,限制(前面的字符的)个数是,至少1个,最多2个。所以此处就是去限定前面的,反斜杠字符本身,所以加起来,就是\\{1,2},而对应的含义也就是

去匹配,至少一个反斜杠,最多2个反斜杠。

所以,目前已拆分出共4个子表达式了:

1 ^
2 [A-Z]
3 :
4 \\{1,2}

 

5.再往后面分析,是左中括号'[‘,根据正则表达式的语法,和前面已经讨论过一次中括号的用法,我们可以知道,后面一定还有一个右中括号,所以,把左右中括号,以及中间内容,都一起写出来,就是:

[^/:\*\?<>\|]

但是,对于中括号中间的这么一堆字符:

^/:\*\?<>\|

至少看起来,也还是比较复杂的。

再但是呢,对于已经了解正则表达式语法的你,应该知道,中括号内,表示取反的写法是,对应的字符或字符集,在其前面,添加上那个特殊字符,向上的箭头,此处叫做插入字符’^’,表示针对某个,或某些字符,取反的意思,即匹配除了这些字符之外的那些字符。

而此处,就是对应的

对于

/:\*\?<>\|

前面加上个插入符号’^’,变成:

^/:\*\?<>\|

表示,匹配,除了 字符组合:

/:\*\?<>\|

之外的字符。

而此处的字符组合:

/:\*\?<>\|

其实就是一堆的字符,一点点写出来的,其详细含义,我们后续再分析。

 

此处还没完,因为此处的[^xxx]的形式之后,还有个加号’+’,对应含义也很明确,就是前面那种字符,即除了/:\*\?<>\|之外的字符,的个数,此处通过加号去限定为,至少是1个,可以更多个,即>=1的个数。

所以,算是[^xxx]+的形式了,其中xxx是/:\*\?<>\|

 

因此,此处已经共分析出5个子表达式了:

1 ^
2 [A-Z]
3 :
4 \\{1,2}
5 [^/:\*\?<>\|]+

 

6.再往后看,就是一个反斜杠’\’加上一个点’.’,即’\.’,其表示点字符本身,这点你也应该在学习正则表达式基本语法的时候,有所了解。

此处再多解释一句就是,之所以不直接写点’.’,是因为字符点’.’本身,在正则表达式中,是匹配任意一个单个字符的意思,而想要匹配这样的,在正则表达式中被用于表示的含义的字符的时候,就需要用到反斜杠,反斜杠用来表示所谓的转义。

在正则表达式中,常见的就有:

特殊字符 正则表达式中所代表的特殊含义 想要匹配对应的字符本身的写法
. 任意单个字符 \.
? 限定符,表示0或1个 \?
* 限定符,0或多个 \*
( , ) 左右括号联合起来,表示一个group组 \( , \)
[ , ] 左右中括号括起来,表示字符集合 \[ ,  \]

 

因此,此处一共已拆分出6个子正则表达式了:

1 ^
2 [A-Z]
3 :
4 \\{1,2}
5 [^/:\*\?<>\|]+
6 \.

 

7.再往后看,后面是一个左括号'(‘,很明显,此处后面肯定有一个右括号,和此处的左括号联合起来,表示一个组group。

此处,很简单,就可以看出来是

(jpg|gif|png|bmp)

注:更复杂的正则表达式,可能会出现多个group嵌套的情况,即括号内嵌套括号的情况,此时,此种拆分方法仍然有效,还是找到最开始的左括号,此时对于括号层次来说肯定是最外层,所匹配的那个的最外层的右括号,那这一部分拿出来,继续分析即可。如果存在更多曾的括号嵌套括号,仍然是找到对应匹配的括号即可。

而对于此处的group组:

(jpg|gif|png|bmp)

的含义,后面再详细分析。

此时,也已经拆分出来,共7个子表达式了:

1 ^
2 [A-Z]
3 :
4 \\{1,2}
5 [^/:\*\?<>\|]+
6 \.
7 (jpg|gif|png|bmp)

 

8.最后,还剩下一个美元符号’$’,表示匹配字符串末尾,这个很好理解。不多解释。

此时,就已经实现了,把上述的一个复杂的正则表达式,拆分成多个逻辑上独立的,共8个,子正则表达式了:

1 ^
2 [A-Z]
3 :
4 \\{1,2}
5 [^/:\*\?<>\|]+
6 \.
7 (jpg|gif|png|bmp)
8 $

 

 

看到这里,对于如何从左往右看,一点点根据逻辑组合,去拆分成多个子表达式,的总体方法和思路,应该大概清楚了。

余下的事情,就是自己通过多读多看多学习,去了解别人写的正则表达式,用此套分析方法,去拆分了。

知道了方法,加上尽量多的练习,自然会对正则表达式,越来越熟悉,越来越理解的。

 

此处,对于此正则表达式的分析,还没完。因为还有几个字正则表达式的含义,没有完全分析透彻。

下面先来总结一下,已经知道的,各个子表达式的含义:

子正则表达式序号 子正则表达式内容 子正则表达式的含义 仍需后续分析的部分子表达式
1 ^ 匹配字符串的开始  
2 [A-Z] 匹配单个字符,此单个字符可能是A-Z中的任何一个  
3 : 匹配冒号字符’:’本身  
4 \\{1,2} 匹配反斜杠字符,最少1个,最多2个  
5 [^/:\*\?<>\|]+ 匹配除了 /:\*\?<>\| 之外的其他字符,个数上则是尽可能多个 /:\*\?<>\| 的含义
6 \. 匹配字符点’.’本身  
7 (jpg|gif|png|bmp) 匹配group,group内部是jpg|gif|png|bmp jpg|gif|png|bmp 的含义
8 $ 匹配正则表达式的末尾  

 

很明显,还剩两个我们没有分析,下面就来详细分析解释其含义:

1./:\*\?<>\| 的含义

其实,理论上,对于这样的字符串:

/:\*\?<>\|

其实也是继续将其按照上述方法,去将其拆分为不同的子表达式。

只是由于此处看似复杂,其实还是很简单,所以,直接分析一下,即可看出其含义。就不详细拆分了。

此处,根据字符本身含义,依次是:

/ 斜杠字符本身
: 冒号字符本身
\* 星号字符’*’本身
\? 问号字符’?’本身
< 小于号字符'<‘本身
> 大于号字符’>’本身
\| 竖线字符’|’本身

 

所以,此部分

 

的总体含义就是:

字符,斜杠,冒号,星号,问号,小于号,大于号,竖线,这些字符(集合)

而放到[^xxx]里面,变成:

[^/:\*\?<>\|]

的意思就是

除了字符:

斜杠,冒号,星号,问号,小于号,大于号,竖线

这些字符之外的,其他的任意字符

而再加上之前的加号’+’去限定其个数是最少1个,>=1个,变成:

[^/:\*\?<>\|]+

所表示的意思就很清楚了:

去匹配 尽可能多个字符,这些字符是:

除了字符:

斜杠,冒号,星号,问号,小于号,大于号,竖线

之外的,其他的任意的字符

 

到此,对此

[^/:\*\?<>\|]+

的含义,才算基本明确。

而如果你本身对于windows等操作系统对于文件名或者路径字符的限制有了解的话,你会发现,这基本上就是

我们所常见的,对于你在windows中,问文件或文件夹命名时,其所提示的,不允许你名字中包含这类:

斜杠,冒号,星号,问号,小于号,大于号,竖线

即:

/,:,*,?,<,>,|

 

而此时,如果你稍微会点举一反三/触类旁通的思想的话,就会联想到,此处去匹配的东西,很可能是文件或文件夹的名字方面的东西。

 

2.jpg|gif|png|bmp 的含义

此处的正则表达式,很明显看出就是:

xxx|xxx|xxx

的格式,其中xxx分别是,具有不同可能的字符串,即多个可能性之一

对应的,其所表达的意思是,去匹配:

要么是jpg,要么是gif,要么是png,要么是bmp

(除了这几种可能外,其他的都不匹配)

 

对于这种匹配多种可能性的正则的写法,想要深入了解的话,可以参考教程:

【教程】详解Python正则表达式之: ‘|’ vertical bar 竖杠

 

所以,此时,我们就可以把每部分的内容的含义,都完整分析出来了:

子正则表达式序号 子正则表达式内容 子正则表达式的含义
1 ^ 匹配字符串的开始
2 [A-Z] 匹配单个字符,此单个字符可能是A-Z中的任何一个
3 : 匹配冒号字符’:’本身
4 \\{1,2} 匹配反斜杠字符,最少1个,最多2个
5 [^/:\*\?<>\|]+ 匹配 >=1个,但尽可能多的,除了斜杠,冒号,星号,问号,小于号,大于号,竖线之外的其他的任意字符
6 \. 匹配字符点’.’本身
7 (jpg|gif|png|bmp) 匹配要么是jpg,要么是gif,要么是png,要么是bmp
8 $ 匹配正则表达式的末尾

 

所以,把这些各个子正则的含义,连接在一起,就可以用语言表示为:

去匹配一个字符串,

该字符串,开头部分,就一个字母,该字母可能是从A到Z的任何一个字母,

后面跟着一个冒号,

再后面是1个或2个反斜杠,

然后是至少一个,但尽量多的,除了斜杠,冒号,星号,问号,小于号,大于号,竖线之外的其他的任意字符,

然后是字符点,

然后以jpg,gif,png,bmp中的其中一个而结尾

 

而对应的,由于之前还有flag标志,表示忽略大小写,则所匹配的内容,就是上述内容的表述,再加上一个,期间部分大小写,就可以了。

所以,最终所要表述的含义就是:

去匹配一个字符串, 期间字母不分大小写,

该字符串,开头部分,就一个字母,该字母可能是从A到Z的任何一个字母,

后面跟着一个冒号,

再后面是1个或2个反斜杠,

然后是至少一个,但尽量多的,除了斜杠,冒号,星号,问号,小于号,大于号,竖线之外的其他的任意字符,

然后是字符点,

然后以jpg,gif,png,bmp中的其中一个而结尾

 

由此,我们可以随便写出来一个,符合该规则的字符串,比如:

a:\123abc.jpg

a:\\123abc.bmp

a:\\123abcdef.jpg

A:\\123abcdef.jpg

E:\\abc123.png

等等,诸如此类的字符串。

 

此时,已可很明显看出来其用意了,其就是要去匹配:

Windows类系统(如XP,Win7等)中,本地的某个磁盘分区根目录下的某张图片而已。

 

至此,算是完整的,从开始的,无法用肉眼一眼就看出来含义的,那个复杂的,正则表达式,将其一点点拆分,分成多个子表达式,各个击破其子表达式的含义,最终再把每个子表达式的含义合成在一起,再加上对应的flag标志的影响,最终生成了复杂表达式的最终含义,以及,用文字描述出来,最终,领悟和理解,原始的正则表达式,所要表示的含义。

 

通过此分析过程可见,其实再复杂的表达式,也都是可以通过拆分的方法,由繁化简,而逐个击破,了解细节的含义,再整合出宏观上的整体的含义,最终搞懂完整的表达式的含义的。

只是过程,或繁或简,取决于表达式本身的复杂程度,以及你本身所对正则表达式的理解和掌握的程度。

 

【总结】

千言万语总结出几句话:

1. 对于复杂的正则表达式,即使从左往右,一点点分析,拆分出多个子正则表达式,然后各个击破,搞懂其含义,最后再合成一个总体的含义,即可实现,将复杂的正则表达式,翻译成人类可以读懂的含义了。

2.再复杂的正则表达式,花足够的时间去分析,都是能搞懂的。 只不过具体要花多长时间,则因人而异。

3.想要尽快的,准确的理解原正则表达式所要描述的含义,还是要多多练习,最终达到熟能生巧,以至于触类旁通的效果。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!