正则表达学习

正则表达学习

在线正则表达式测试地址 <- 点我

Sample1:

找出以 lefelefe_x 单词开头,以 wsy 结尾的字符串。比如:lefe name is wsy 是合法的,而 lef name is wsy 是非法的。

正则表达式为:(^(?:lefe|lefe_x)\b.{0,}wsy$)

1.^表示从字符串的开始位置匹配,^(?:lefe|lefe_x) 表示以 lefe 或者 lefe_x 开头;
2. | 表示或,比如 A | B | C ,表示 A,B 和 C 中任意一个;
3. () 表示一个组,(?:) 表示不捕获这个分组;
4. \b 表示匹配一个单词的边界,在这里只能匹配 lefelefe_x;
5.匹配字符串的开头和结尾后,基本上完成了题目的要求,但是字符串lefe(lefe_x)和wsy之间可以是任意字符,. 表示匹配任意字符(不包含换行符),{0,}表示匹配0个或多个字符,则.{0,} 表示匹配0个或多个任意字符(不包含换行符);
6.$表示从字符串的结尾处开始匹配,wsy$ 则表示以 wsy 结尾;

【 知识点 】

  1. 字符边界
    ^$\b 表示字符的边界,^ 匹配字符串的开头,$匹配字符串的结尾,\b匹配单词的边界,如:lefe\b 可以匹配 lefe ,但不可匹配 lefe_x
  2. 量词
    {m} 只出现 m 次,lefe{2} 只能匹配 lefee
    {m,n} 出现 m 到 n 次
    {m,} 至少出现 m 次
  3. 选择表达式
    比如 lefe | Lefe_x | Lefe ,表示 lefe,Lefe_x 和 Lefe 中任意一个
  4. 分组
    以括号括起来的字符集为一个分组,在 ( 添加 ?: 将忽略这个分组 )
  5. 字符集
    . 匹配除换行符以外的任意一个字符
    \w = [0-9a-zA-Z_]
    \W = [^0-9a-zA-Z_]
    \s = [\t\n\v]
    \S = [^\t\n\v]
    \d = [0-9]
    \D = [^0-9]

正则表达式分组()、不捕获(?:)

分组

分组在正则中用()表示,分组的作用有两个:

1.将某些规律看成是一组,然后进行组级别的重复,可以得到意想不到的效果。

2.分组之后,可以通过后向引用简化表达式(\1 或者$1)。

分组举列

先来看第一个作用,对于IP地址的匹配,简单的可以写为如下形式:

\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}

但仔细观察,可以发现一定的规律,可以把.\d{1,3}看成一个整体,也就是把他们看成一组,再把这个组重复3次即可。表达式如下:

\d{1,3}(.\d{1,3}){3}

再来看第二个作用,就拿匹配xxx标签来说,简单的正则可以这样写:

<title>.*</title>

可以看出,上边表达式中有两个title,完全一样,其实可以通过分组简写。表达式如下:

<(title)>.*</\1>

对于分组而言,整个表达式永远算作第0组,在本例中,第0组是<(title)>.*</\1>,然后从左到右,依次为分组编号,因此,(title)是第1组。 [(xxx) 一个括号等于一个分组]

注意:

用\1这种语法,可以引用某组的文本内容,但不能引用正则表达式。

例如刚刚的IP地址正则表达式为\d{1,3}(.\d{1,3}){3},里边的\d{1,3}重复了两次,如果利用后向引用简化,表达式如下:

错误示范:
(\d{1,3})(.\1){3}

后向引用,引用的仅仅是文本内容,而不是正则表达式!

也就是说,组中的内容一旦匹配成功,后向引用,引用的就是匹配成功后的内容,引用的是结果,而不是表达式。

因此,(\d{1,3})(.\1){3}这个表达式实际上匹配的是四个数都相同的IP地址,比如:123.123.123.123。

不捕获

不捕获就是在分组的前边加上?: ,可以在不需要捕获分组的表达式中使用,加快表达式执行速度。

就拿匹配xxx标签来说,通过分组可以简写为

<(title)>.*</\1>

但是如果是 (?:title) ,则\1就不能捕获到这个子组了,只能捕获第一个出现的非 ?: 的分组作为\1

同时注意(?:title)本身会在完整匹配中,只是不在子组中,注意和断言的区别

1
2
3
4
5
6
7
8
9
10
$str="ab123ff";
//正常,完整匹配为ab123ff, 有两个子组ab, ff
preg_match_all("|([a-z]{2}).*([a-z]{2})|U", $str, $out);echo "<pre>";
print_r($out);
echo "</pre>";

//不捕获分组,完整匹配为ab23ff, 有一个子组ff
preg_match_all("|(?:[a-z]{2}).*([a-z]{2})|U", $str, $out);echo "<pre>";
print_r($out);
echo "</pre>";

Sample2 :

匹配以 lefe 开头,lefe 后面不能紧跟_x,后面由数字,-,_和字母组成,且不包含l,e,f 三个字母中的任意一个(至少一位),且为最短的用户名

【分析】
根据要求可以把题目拆分成:
1.以 lefe 开头,不能为 lefe_x。可以转换成 ^lefe(?!_x);
2.中间部分由数字,-,_和字母组成,不能包含 l,e,f,至少一位。可以转换成 [0-9a-dg-km-zA-DG-KM-Z_-]+;
3.匹配最短的。在正则表达式中用到了贪婪与非贪婪的概念,使用 ?;

【正则表达式】
^(lefe(?!_x)(?:[0-9a-dg-km-zA-DG-KM-Z_-]+?)

知识点

1.非获取匹配
lefe(?=_x) ,lefe 后面紧跟着 _x,正向肯定预查
lefe(?!_x) ,lefe 后面不能出现 _x,正向否的定预查
(?<!lefe)_x ,_x 前面不能为 lefe,反向肯定预查
(?<=lefe)_x ,_x 前面为 lefe,反向否的定预查
2.字符集合
[xyz] 匹配 x,y,z 任意一个字符;
[^xyz] 匹配除 x,y,z 外的任意一个字符;
[a-z] 匹配 a-z 中任意一个字符;
3.贪婪匹配与非贪婪匹配
默认的匹配规则为非贪婪匹配,使用 ? 使贪婪匹配变为非贪婪匹配。比如:使用正则表达式lefe{2,} 匹配 lefeeeeeeee,如果为贪婪匹配(lefe{2,})时将匹配 lefeeeeeeee ,为非贪婪匹配(lefe{2,}?)时,将匹配 lefee
4.量词 *+?
'lefe*' ,lef 后有0个或多个e
'lefe+' ,lef 后有1个或多个e
'lefe?' ,lef 后有0个或1个e

Author

Sylar

Posted on

2019-04-28

Updated on

2021-11-14

Licensed under

Comments