首页 > 代码库 > 如何教你看懂复杂的正则表达式

如何教你看懂复杂的正则表达式

【前言】

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

如何教你看懂复杂的正则表达式