zoukankan      html  css  js  c++  java
  • 如何教你看懂复杂的正则表达式

    【前言】

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

  • 相关阅读:
    javascript和C#比较
    前端模块管理器简介
    javascript中的splice方法介绍&示例
    javascript中数组揭秘
    17款code review工具
    IIS ip访问限制插件
    iis 限制动态IP地址访问次数
    AWS云使用100条宝贵经验分享
    C# 开源框架(整理)
    如何获取Azure AD tenant的tenant Id?
  • 原文地址:https://www.cnblogs.com/superstar/p/6638970.html
Copyright © 2011-2022 走看看