Andy Niu �����ĵ�

Andy Niu

Andy Niu Help  1.0.0.0
正则表达式

模块

 常用正则表达式
 

变量

 特殊字符和转义
 
 匹配优先和忽略优先
 
 括号的用处
 
 环视
 
 分组_捕获_反向引用
 
 分组_不捕获
 
 固化分组
 
 占有优先
 

详细描述

变量说明

分组_不捕获
1、在分组_捕获_反向引用一节中,我们对每个分组都进行了捕获,比如查找重复出现的[a-zA-Z]+\d,
    使用([a-zA-Z]+)(\d)(\1)(\2),(\1)(\2) 我们不需要使用,也进行了捕获,也就是说正则表达式
    引擎必须记住(\1)(\2)的内容,这会导致额外的开销。
2、怎么解决?
    解决办法是使用分组不捕获,如下:
    ([a-zA-Z]+)(\d)\1\2
    注意:这个时候使用notepad++进行替换的时候,只捕获了两个分组,$1对应([a-zA-Z]+),$2对应(\d)
    没有$3和$4,也可认为$3和$4对应为空。
3、考虑下面的需求,找出[a-zA-Z]+\d[a-zA-Z]+,其中[a-zA-Z]+相同,也就是说\d不需要捕获,使用分组不捕获,如下:
    ([a-zA-Z]+)(?:\d)\1
4、在notepad++,匹配的时候使用\n反向引用第n个捕获到的分组,替换的时候使用$n替换第n个捕获到的分组,
    有些程序支持命名捕获分组,对捕获到的分组,取一个名字,后面根据名称引用。
5、注意:\d等价于[0-9],还有一个常用的缩写\w,但是不推荐使用\w,因为不同的程序对\w的解释不同,
    有些等价于[a-zA-Z0-9],有些等价于[a-zA-Z0-9_],有些等价于所有的显示字符,包括等号减号等,
    为了兼容,不推荐使用\w
参见
分组_捕获_反向引用
分组_捕获_反向引用
1、考虑下面的需求,对于文本
    Andy1Andy1
    Bill3Bill4
    Caroine5Caroine5
    找出重复出现的[a-zA-Z]+\d,也就是说Andy1Andy1匹配成功,Bill3Bill4匹配失败。
2、使用分组_捕获_反向引用,如下:
    ([a-zA-Z]+)(\d)(\1)(\2)
    (\1)反向引用([a-zA-Z]+),然后本身又作为一个捕获分组。
3、这里捕获了4个分组,使用notepad++进行替换的时候,$1对应([a-zA-Z]+),$2对应(\d),
    $3对应(\1),$4对应(\2)
4、考虑下面的需求,找出连续出现3次[a-zA-Z]+\d的文本,如下:
    ([a-zA-Z]+)(\d)(\1)(\2)(\1)(\2),当然也可以使用 ([a-zA-Z]+)(\d)(\1)(\2)(\3)(\4)
参见
分组_不捕获
匹配优先和忽略优先
1、考虑下面的需求,"Tom" is short for "Thomas" 找出所有的双引号括住的内容。以notepad++为例说明。
2、容易想到的办法:查找 ".*" 发现找出的结果是 "Tom" is short for "Thomas"
3、这不是我们所期望的,为什么?
    因为.* 是匹配优先,即贪婪模式,也就是说会尽量多吃。
4、怎么解决这个问题?
    使用忽略优先,也就是尽量少吃,如下:".*?"
    对于不支持忽略优先的程序,可以使用"[^"]*" ,实现忽略优先。
    "[^"]*" 表示可以尽量多吃,但是不能吃",遇到第一个"就要停下来。
参见
占有优先
1、占有优先和固化分组一样,解决同样的问题,吃下去,丢弃备用状态,以后不会吐出来。
2、占有优先的语法如下:
    ([a-zA-Z]++):
参见
固化分组
1、考虑下面的需求,使用[a-zA-Z]+: 去匹配文本,对于文本Subject,分析匹配过程。
2、[a-zA-Z]+匹配优先,一直吃到t,发现冒号不能匹配,于是使用备用状态,吐出一个字符,
    还是不能匹配,再吐出一个字符......一直继续下去。
3、这里存在问题,引擎试图吐出一个字符匹配冒号,我们知道这是徒劳的尝试。因为[a-zA-Z]+没有吃下去冒号,当然不可能吐出冒号。
    如果正则表达式引擎足够聪明,意识到这一点,就不需要尝试吐出字符,直接报告匹配失败,也就是说,之前的备用状态应该丢弃。
    但是,一般情况下,引擎没有这么聪明,需要人为的协助,提示引擎不需要吐出字符,也就是丢弃之前的备用状态。
4、怎么提示引擎呢?
    使用固化分组,如下
    (?>[a-zA-Z]+): (?>[a-zA-Z]+)作为一个整体,吃下去文本,即使后面匹配失败,也不吐出来。
5、也就是说,使用固化分组可以提前报告匹配失败,不需要进行徒劳的尝试,提高效率。
6、对于不支持固化分组的程序,可以使用占有优先和环视。
    占有优先,如下 ([a-zA-Z]++):
    环视匹配一个位置,有一个重要的特点,就是匹配尝试结束后,不会留下任何备用状态,因此,使用环视解决上面的问题,如下:
    (?=([a-zA-Z]+))\1:  (?=([a-zA-Z]+))匹配一个位置,右边是([a-zA-Z]+),\1反向引用([a-zA-Z]+),后面跟着一个冒号。
参见
括号的用处
1、限定多选项的范围
    gr(a|e)y,查找 gray和grey,等价于gr[ae]y
2、多个字符括住,合成一个单元,接受量词的限制
    (th){2},查找th出现量词
3、分组,反向引用
    表达式:([a-z]{3})([0-9]{2})\1\2 ,匹配下列文本,
    abc12def12  // 匹配失败
    thy34thy34  // 匹配成功
4、环视功能
    (?=\d)      顺序环视(右边包含一个数字)
    (?<=\d)     逆序环视(左边包含一个数字)
    =换成!        变成否定环视,左边或右边不包含一个数字。
参见
特殊字符和转义
1、正则表达式中有些字符用作特殊的意义,比如元字符* 表示匹配0个或多个,小括号() 表示分组。
2、这些字符表示特殊的意义,如何表示这些字符本身呢?使用转义字符\,告诉程序右斜杠后面的字符
    是字符本身。比如:   shell中,双引号用作框住字符串,要表示双引号本身,需要转义,echo "\"Hello\""
    特别注意:不同程序,所包含的特殊字符不一样。同时是否转义的说明也不同,比如:
    在notepad++中,+ 表示匹配1个或多个,\+ 表示+字符本身
    在vim中,+ 就是+字符本身,\+匹配一个或多个
参见
环视
1、环视就是看周围的意思,环视匹配一个位置,这个位置的周围满足一些条件。
    也就是这个位置的左右,包含某些内容,或者不包含某些内容。
2、考虑下面的需求,对于12,345,678,修改为 12,345,678。
    这个需求要做的事情就是在数字中的一些位置添加逗号,这个位置的特点是:
    前面匹配一个数字,后面匹配三个数字的整数倍,结尾不是数字或者是换行符。
3、解决办法:
    查找 (?<=\d)(?=(\d\d\d)+([^\d]|$)) 替换为逗号。也就是查找一个位置,替换为逗号。
4、匹配流程如下:
    先匹配1,后面有7个数字,不是3的倍数,匹配失败。
    再匹配2,后面有6个数字,是3的倍数,匹配成功,替换。
    再匹配3,后面有5个数字,是3的倍数,匹配失败。
    ......
5、环视还有一个重要的特点,就是匹配尝试结束后,不会留下任何备用状态,利用这一点可以解决固化分组的问题。
参见
固化分组
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.