Zhaoyun's Blog

往美得想,往好得做!

正则表达式笔记

2018-08-17 posted in [技术]

正则表达式笔记

一、内容表示

1、常规字符,除特殊字符使用斜杠(\)转义

2、点(.),代表除换行符号外的任意字符

3、字符集合,使用匹配中括号中的任意一个字符,如:[aeiou]、[a-zA-Z]

4、特定字符:任意字母数字下划线(\w)、任意空白(\s)、数字(\d)、单词边界(\b)、行头(^)、行尾($)

5、反义:非字母数字下划线(\W)、非空白(\W)、非数字(\D)、非单词边界(\B)、除特定字符外([^x]、[^xyz])

二、重复表示

1、出现 1 次以上(+)

2、出现任意次(*)

3、出现 1 次或不出现(?),注意只现一次的情况下,直接使用内容表示字符,不需要重复表示

4、出现 n 次({n})

5、出现 n 次以上({n,})

6、出现 m 至 n 次({m,n})

三、分组

1、查找IP地址的示例:

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

2、在分组中使用“或”,如以下可限制数字大小的IP地址限定规则

((2[0-4]\d|25[0-5]|[01]?\d\d?).){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

3、IP地址或域名:

((\d{1,3}.){3}\d{1,3}|([a-zA-Z]+.)+[a-zA-Z]+)

四、后向引用

每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

查找叠词(如:go go, no no):

\b(\w+)\b\s+\1\b

你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:

(?\w+)(或者把尖括号换成'也行:(?'name'\w+)),这样就把\w+的组名指定为name了。

要反向引用这个分组捕获的内容,你可以使用\k,所以上一个例子也可以写成这样:

\b(?\w+)\b\s+\k\b 或 \b(?'g1'\w+)\b\s+\k'g1'\b

其实,组号分配还不像我刚说得那么简单:

分组0对应整个正则表达式 ,实际上组号分配过程是要从左向右扫描两遍的:

第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号

你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.

五、零宽断言

(?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。

比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I’m singing while you’re dancing.时,它会匹配sing和danc。

(?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。

比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。

假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:

((?<=\d)\d{3})+\b,用它对1234567890进行查找时结果是234567890。

使用这个表示式:(?<=\d)(\d{3})+\b,似乎更好理解!

下面这个例子同时使用了这两种断言:(?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)。

六、负向零宽断言

零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。

如果我们想查找这样的单词–它里面出现了字母q,但是q后面跟的不是字母u,负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。

现在,我们可以这样来解决这个问题:\b\wq(?!u)\w\b。

同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。

一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。

七、注释

小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249) 250-5 [01]?\d\d?(?#0-199)。

要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。

启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:

(?<= # 断言要匹配的文本的前缀

<(\w+)> # 查找尖括号括起来的字母或数字(即HTML/XML标签)

) # 前缀结束

.* # 匹配任意文本

(?= # 断言要匹配的文本的后缀

<\/\1> # 查找尖括号括起来的内容:前面是一个”/”,后面是先前捕获的标签

) # 后缀结束

八、贪婪与懒惰

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。

以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。

这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。

现在看看懒惰版的例子吧:

a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。

为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?

简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权。

表5.懒惰限定符

*? 重复任意次,但尽可能少重复

+? 重复 1 次或更多次,但尽可能少重复

?? 重复 0 次或 1 次,但尽可能少重复

{n,m}? 重复 n 到 m 次,但尽可能少重复

{n,}? 重复 n 次以上,但尽可能少重复

九、平衡组/递归匹配

这里介绍的平衡组语法是由.Net Framework支持的;其它语言/库不一定支持这种功能,或者支持此功能但需要使用不同的语法。

有时我们需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用(.+)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。

有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?

为了避免(和(把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把xx <aa aa> yy这样的字符串里,最长的配对的尖括号内的内容捕获出来?

这里需要用到以下的语法构造:

(?’group’) 把捕获的内容命名为group,并压入堆栈(Stack)

(?’-group’) 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败

(?(group)yes no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分

(?!) 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败

如果你不是一个程序员(或者你自称程序员但是不知道堆栈是什么东西),你就这样理解上面的三种语法吧:

第一个就是在黑板上写一个”group”,

第二个就是从黑板上擦掉一个”group”,

第三个就是看黑板上写的还有没有”group”,如果有就继续匹配yes部分,否则就匹配no部分。

我们需要做的是每碰到了左括号,就在压入一个”Open”,每碰到一个右括号,就弹出一个,到了最后就看看堆栈是否为空

--如果不为空那就证明左括号比右括号多,那匹配就应该失败。

正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符),尽量使整个表达式得到匹配。

< #最外层的左括号

​ [^<>]* #最外层的左括号后面的不是括号的内容

​ (

​ (

​ (?’Open’<) #碰到了左括号,在黑板上写一个”Open”

​ [^<>]* #匹配左括号后面的不是括号的内容

​ )+

​ (

​ (?’-Open’>) #碰到了右括号,擦掉一个”Open”

​ [^<>]* #匹配右括号后面不是括号的内容

​ )+

​ )*

​ (?(Open)(?!)) #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的”Open”;如果还有,则匹配失败

> #最外层的右括号

平衡组的一个最常见的应用就是匹配HTML,下面这个例子可以匹配嵌套的<div>标签:

<div[^>]>[^<>](((?’Open’<div[^>]>)[^<>])+((?’-Open’</div>)[^<>])+)(?(Open)(?!))</div>.

十、尚未讨论的语法

\a 报警字符(打印它的效果是电脑嘀一声)

\b 通常是单词分界位置,但如果在字符类里使用代表退格

\t 制表符,Tab

\r 回车

\v 竖向制表符

\f 换页符

\n 换行符

\e Escape

\0nn ASCII代码中八进制代码为nn的字符

\xnn ASCII代码中十六进制代码为nn的字符

\unnnn Unicode代码中十六进制代码为nnnn的字符

\cN ASCII控制字符。比如\cC代表Ctrl+C

\A 字符串开头(类似^,但不受处理多行选项的影响)

\Z 字符串结尾或行尾(不受处理多行选项的影响)

\z 字符串结尾(类似$,但不受处理多行选项的影响)

\G 当前搜索的开头

\p{name} Unicode中命名为name的字符类,例如\p{IsGreek}

(?>exp) 贪婪子表达式

(?-exp) 平衡组

(?im-nsx:exp) 在子表达式exp中改变处理选项

(?im-nsx) 为表达式后面的部分改变处理选项

(?(exp)yes no) 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no

(?(exp)yes) 同上,只是使用空表达式作为no

(?(name)yes no) 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no

(?(name)yes) 同上,只是使用空表达式作为no

十一、处理选项

IgnoreCase 匹配时不区分大小写。

Multiline 多行模式,更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。

​ *** 在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.

Singleline 单行模式,更改.的含义,使它与每一个字符匹配(包括换行符\n)。

IgnorePatternWhitespace 忽略表达式中的非转义空白并启用由#标记的注释。

ExplicitCapture 仅捕获已被显式命名的组。

在git shell中显示中文文件名

2018-05-04 posted in [技术]

读懂「10万+」标题的全部套路

2018-03-20 posted in [设计]

扁平化与拟物设计对比

2018-03-20 posted in [设计]

拟物化不是扁平化的对立面,而是作为扁平化的一部分永存

2018-03-20 posted in [设计]

裁剪图片让PPT呈现更好视觉效果

2018-03-19 posted in [设计]

PPT中如何处理表格

2018-03-19 posted in [设计]

PPT高手配图

2018-03-19 posted in [设计]

信息量大的PPT表格如何突出重点

2018-03-19 posted in [设计]

PPT表格优化指南

2018-03-19 posted in [设计]

PPT排版中少有人知的几个细节

2018-03-19 posted in [设计]

留白 提升PPT排版美感

2018-03-19 posted in [设计]

分校排版 搞定内容较多的PPT

2018-03-19 posted in [设计]

巧借PPT图表,更好地展示工作业绩

2018-03-19 posted in [设计]

巧借形状,做出更有创意的PPT

2018-03-19 posted in [设计]

git 配置备忘

2018-03-16 posted in [技术]

vi常用命令

2018-03-16 posted in [技术]

白话数字签名

2018-03-15 posted in [技术]

GIT简单教程

2018-03-15 posted in [技术]

gitup

2018-03-15 posted in [技术]

TypeScript 学习笔记

2018-03-15 posted in [技术]

JavaScript学习笔记

2018-03-15 posted in [技术]

Ionic2 使用iconfont图标

2018-03-15 posted in [技术]

npm添加淘宝镜像

2018-03-15 posted in [技术]

Ionic打包的时候自动签名

2018-03-15 posted in [技术]

解读 六如偈

2018-03-08 posted in [故事]

人与佛关于婚外情的对话

2018-03-08 posted in [故事]

国民党抗战记实资料

2018-03-08 posted in [故事]

胡涟祭天词及家书

2018-03-08 posted in [故事]

值得玩味的哲学

2018-03-08 posted in [故事]

一个很形象的房产笑话

2018-03-08 posted in [故事]

以贪官治贪官

2018-03-08 posted in [故事]

丢猫千万别找设计师

2018-03-08 posted in [故事]

写给大家看的中文排版指南

2018-03-08 posted in [设计]

不要假想 去做 去试错

2018-03-08 posted in [故事]

空色转换

2018-03-08 posted in [故事]

判断一个项目的大致方向

2018-03-08 posted in [产品]

20种常见的构图错误

2018-03-07 posted in [设计]

风景画的构图规则

2018-03-07 posted in [设计]

张朝忠给我的职场建议

2018-03-07 posted in [故事]

从排队等待谈进度条设计

2018-03-07 posted in [产品]

互联网付费产品设计

2018-03-07 posted in [产品]

星巴克的积分体系设计

2018-03-07 posted in [产品]

通过留白打造简洁有效的设计

2018-03-07 posted in [设计]

移动应用的【空】状态界面设计

2018-03-07 posted in [设计]

十个经典的移动APP UI设计模式

2018-03-07 posted in [设计]

尼尔森十大可用性原则

2018-03-07 posted in [设计]

色彩与心理感受

2018-03-07 posted in [设计]

PPT课堂有感觉的点滴

2018-03-07 posted in [设计]

Mongo 学习笔记

2018-03-07 posted in [技术]

重要十句

2018-03-07 posted in [故事]

乔布斯2005斯坦福大学演讲稿

2018-03-07 posted in [故事]

80端口绑定失败

2018-03-07 posted in [技术]

阴影是条纸龙

2018-03-07 posted in [故事]

生命的价值

2018-03-07 posted in [故事]

推动用户分享

2018-03-07 posted in [产品]

成功并不像你想像的那么难

2018-03-07 posted in [故事]

判断项目是否靠谱

2018-03-07 posted in [产品]

人生际遇与机遇

2018-03-07 posted in [故事]

产品经理必读的九步法

2018-03-07 posted in [产品]