是我
lllhy

正则表达式笔记

先引用网上看到的一段话:

正则表达式具有伟大技术发明的一切特点,它简单、优美、功能强大、妙用无穷。对于很多实际工作来讲,正则表达式简直是灵丹妙药,能够成百倍地提高开发效率和程序质量。

我觉得他说的不对,我们稍微改下:

正则表达式具有伟大技术发明的一切特点,它简单、优美 不优美、功能强大、妙用无穷。对于很多实际工作来讲,正则表达式简直是灵丹妙药,能够成百倍地提高开发效率和程序质量。

为什么说他不优美,因为正则表达式可读性极差,在我看来绝对不属于优美的范畴。但是他的强大确是无法反驳的。

基础

元字符

image-20210327122118310

反义

使用[^]声明翻译,或是元字符中改为大写,如\B \D \S

量词

先解释关于量词所涉及到的重要的三个概念:

贪婪(贪心) 如”*”字符 贪婪量词会首先匹配整个字符串,尝试匹配时,它会选定尽可能多的内容,如果 失败则回退一个字符,然后再次尝试回退的过程就叫做回溯,它会每次回退一个字符,直到找到匹配的内容或者没有字符可以回退。相比下面两种贪婪量词对资源的消耗是最大的。

懒惰(勉强) 如 “?” 懒惰量词使用另一种方式匹配,它从目标的起始位置开始尝试匹配,每次检查一个字符,并寻找它要匹配的内容,如此循环直到字符结尾处。

占有 如”+” 占有量词会覆盖事个目标字符串,然后尝试寻找匹配内容 ,但它只尝试一次,不会回溯,就好比先抓一把石头,然后从石头中挑出黄金

区间

使用[a-z]表示匹配a-z区间任意字符

简单使用

有了上述基础知识后,我们就可以用上述元字符来写一些简单的正则表达式了,让我们看些例子。

11位手机号

手机号因为以1开头,后续10位数字,所以简单写法如下

^1\d{10}$

邮箱匹配

思路

我们看下邮箱的构成: 邮箱名@n级域名.(m级域名.)(域名后缀)。m级域名可能出现多次,也可能不出现。而邮箱名可以是任意的字母数字下划线和短横线(不考虑中文)。这样我们的匹配规则就很明确了。

patten=^[\w-]+@([\w-]+.)+([a-z]+)$

注:这里只做简单的例子,并不考虑非常复杂、以及特殊的情形。

解释

  • 头部的^声明了^后方的字符应出现在字符串头部。$声明$前方的字符应出现在字符串末尾。所以模式用于匹配整个字符串。

  • 首先这里使用[\w-]用来匹配邮箱名可以是字母数字下划线和短横线,后面的+声明[\w-]匹配的字符必须出现一次或者多次。

  • @用于匹配邮箱名中的@。

  • [\w-]+.用于匹配多级域名中的每一级。由于域名最低为一级域名,所以上述的模式最少出现一次。最终写为([\w-]+.)+

  • ([a-z]+)用于匹配域名后缀,如com、cn等。

进阶

分组

捕获分组

我们使用()对模式进行分组,看个例子

(a|d)*c

上面这个例子里,(a|d)*表示出现a或者d 0次或多次。由于*只匹配前方的单个对象,如果我们需要匹配的条件比较负责,就使用()进行分组。本质和()本身含义一致。

命名

上面已经进行了分组,所以可以使用分组编号(下标来使用),但是如果我想指定名称时,就需要命名分组了。

(?<name>exp)

通过<name>指定命名,如捕获座机号码区号和号码部分:

(?<quhao>\0\d{2})-(?<haoma>\d{8})

用于命名区号和号码的分组捕获。

非捕获分组

使用有时我们设定捕获组只是为了定位目标,但是实际并不需要该组的内容。那么可以利用非捕获组声明不捕获。

用法
(?:exp)

如还是号码问题,如果只需要号码,那么就需要对区号部分声明非捕获组:

 (?:\0\d{2})-(\d{8})

反向引用

先举个例子,如果我们需要捕获一组aabbccdddddgseddbb里成对出现的元素,如aabb这样。那么就需要反向引用了。

首先思路大致是先匹配一个字符,然后查看下一个是否与他相等即可。刚刚分组里已经将了分组是有编号的。所以我们通过编号反向引用前方分组的内容,就可以用来判断两个是否相等了。

用法

number或\name

\1引用第一个分组,\quhao引用名为quhao的分组。

再回到刚刚的问题,那么写法就如下了。

(\w)\1

输出如下:

aa
bb
cc
dd
dd
dd
bb

零宽断言

断言断言表示断定目标中会出现的字符。零宽表示断言所占的宽度为0,即只匹配断言的位置,并不输出。

正向先行断言

正向是指断言的内容为真时匹配,与此相反的还有后面讲到的反向,反向就是只断言内容为假时匹配。先行是指匹配的方向,即内容在前,断言在后。

用法
...(?=patten)

正向后行断言

与线行相反,匹配内容在后,断言在前。看个例子就明白了。

用法
(?<=patten)...

正向先/后行断言例子

假设我们要用爬虫抓取csdn里的文章阅读量。通过查看源代码可以看到文章阅读量这个内容是这样的结构

<span class="read-count">阅读数:641</span>

所以有两种方式

  • 通过前方的<span class="read-count">阅读数:部分定位目标。
  • 通过后方的</span>定位目标
正向先行断言实现
d+(?=</span>)

通过\d+匹配数字。(?=</span>)声明正向先行断言。

正向先行断言实现
(?<=<span class="read-count">阅读数:)\d+

声明正向后行断言,正确匹配。

反向先行断言

和正向先行断言相反,反向先行行断言只匹配与断言不相符的先行部分。

用法
....(?!patten)

反向后行断言

类比上面,反向后行只匹配与断言不相符的后行部分。

用法
(?<!patten)...

反向后行断言例子

假设我们要捕获一个博客页面的标题和文章内容。样例如下。

<div>
    <p class="title">标题</p>
    <p class="content">678</p>
</div>
<div class="foot">页脚</div>

可以看到,标题和内容的格式并不一样,并且还有页脚内容,所以我们需要将页脚内容排除。这里就使用反向后行断言+正向先行断言实现。

(?<!<div class="foot">).+(?=</p>)
赞赏

lhy

文章作者

普通人。

发表评论

textsms
account_circle
email

lllhy

正则表达式笔记
先引用网上看到的一段话: 正则表达式具有伟大技术发明的一切特点,它简单、优美、功能强大、妙用无穷。对于很多实际工作来讲,正则表达式简直是灵丹妙药,能够成百倍地提高开发效率…
扫描二维码继续阅读
2021-03-27