正则表达式(规则表达式)的笔记

本文学自:李笑来老师的《自学是门手艺》

一、基本概念

Wikipedia 上对正则表达式的说明如下:

正则表达式(英语:Regular Expression,在代码中常简写为 regex、regexp 或 RE),又称正规表示式正规表示法正规运算式规则运算式常规表示法,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在 Perl 中就内建了一个功能强大的正则表达式引擎。正则表达式这个概念最初是由 Unix 中的工具软件(例如 sed 和 grep)普及开的。

一下是绝大多数翻译成中文的教程中,对正则表达式进行讲解时所使用的描述:

一个正则表达式(Regular Expression)通常被称为一个模式(Pattern)

对新手来说,刚接触这个概念难免一头雾水,看着每一个自己认识的字,组成起来的词却无法完全理解,会有挺大压力和困扰。

所以,对于新手来说可以这么理解:

一个规则表达式,通常被称为一个规则。

我们可以用书写特定的规则,用来在文本中捕获(Capture)与规则一致的字符串,而后对其进行操作。

所以总结起来:

规则表达式,通常所写为:Regex,是最强大且不可或缺的文本处理工具——它的用处就是在文本扫描/搜索(Scan/Search)与某一规则(Pattern)匹配(Match)的所有实例,并且还可以按照规则捕获(Capture)其中的部分或全部,对他们进行替换(Replace).

二、原子与操作符优先级

只要有运算,就会有操作符(operators)和操作元(operands)

 

原子

Regex中,操作符也有优先级,操作元有个专有名字叫原子(Atom)

1.最基本的原子,就是本义字符,他们都是单个字符

本义字符包括从a到z,A到Z,0到9,还有一个_

上面他们所代表的就是它们的字面值。即,相当于,string.ascii_lettersstring.digits 以及 _

2.以下字符都有特殊含义(后面详细说):

\   +  *  ?  .   -    ^   $   |   ( )   { }   [  ]    <  > 

当你在写Regex的时候,如果需要搜索的字符不是本义字符,而是以上这些特殊字符时,建议都直接加上转义符号 \ 来表示。你想搜索 ' ,那就写 \' ,或者你想搜  #  ,那就写  \#  (这里#号前面不一定非要加  \  因为它不是Regex的特殊符号,所以转义符可有可无。)

集合原子

集合原子还是原子

1.标示方法: [  ]  

[abc]   意思是说 a  or  b  or  c  ,即abc中的任意一个字符。

比如,beg[iau]n 能够代表 beginbegan,以及 begun

2.方括号内的操作符

“[ ]”内可以使用的操作符有两个:-(区间)和  ^(非)

非常容易理解,例子:

[1-5] : 代表1到5的区间内的任意字符

[^abc] : 代表除abc以外的其他任意字符

注意:一个集合原子只能有一个  ^  ,且需要在  [  后面。否则不起作用。

类别原子

指那些能够代表“一类字符”的原子,他们都得使用转义符号再加上另外一个符号表达:

\d : 任意数字,等价于[0-9]                         ====>          了解了“d”代表的意思,就好理解了:digits

\D : 任意非数字,等价于[^0-9]

\w : 任意本义字符,等价于[a-zA-Z0-9_]     ====>          同理“w”:word characters

\W : 任意非本义字符,等价于[^a-zA-Z0-9_]

\s : 任意空白符,相当于[ \f\n\r\t\v]              ====>          同理“s”:spaces

\S : 任意非空白符,相当于[^ \f\n\r\t\v]

. : 除了\r,\n之外的字符,相当于[^\r\n]

在空白的集合中:[ \f\n\r\t\v],\f 是分页符;\n \r 是换行符;\t 是制表符;\v 是纵向制表符(很少用到)。

各个字母是那个词的首字母的话:

  • f 是 flip
  • n 是 new line
  • r 是 return
  • t 是 tab
  • v 是 vertical tab

边界原子

可以用边界原子指定边界,也称为“定位操作符”

^ : 匹配被搜索字符串的开始位置;

$ : 匹配被搜索字符串的结束位置;

\b :匹配单词的边界。er\b,能匹配 coder 中的 er,却不能匹配 error 中的 er(非边界)

\B : 匹配非单词边界。er\B,能匹配 error 中的 er,却不能匹配 coder 中的 er(边界)

代码示例:

import re
str = 'never ever verb however everest'
pttn = r'er\b'
re.findall(pttn, str)
pttn = r'er\B'
re.findall(pttn, str)

上面代码输出结果应该是:

['er', 'er', 'er']

['er', 'er']

组合原子

圆括号()可以多个单字符的原子,组合为一个原子。除了组合这个功能,还有一个功能叫捕获(Capturing)

注意区别,er[er] 和 [(er)](https://regexper.com#(er) 各不相同。

  • er 是两个原子,'e' 和紧随其后的 'r'
  • [er] 是一个原子,或者 'e' 或者 'r'
  • (er) 是一个原子,'er'

数量操作符

有这些:+  ? *  {n, m}

作用:用来限定位于他们之间的原子允许出现的个数;不加数量限定则代表出现一次且仅出现一次。

 + 代表前前面的原子出现次数 >= 1

例: go+gle 可以匹配 google gooogle goooogle

 ? 代表前面的原子最多只能出现一次

例:colou?red 可以匹配 colored 或  coloured  (u字符不出现或者只出现一次)

 * 代表随意。(前面的原子可以出现或不出现,可以出现一次或多次)

例: 520* 可以匹配 52  520  5200  52000000000000 ... 

 {n} 之前的原子出现指定的n次

 {n,} 之前的原子至少出现n次

 {n, m} 之前的原子出现至少n次,至多m次

代码示例:

import re
with open('regex-target-text-sample.txt', 'r') as f:
    str = f.read()

pttn = r'go+gle'
re.findall(pttn, str)

pttn = r'go{2,5}gle'
re.findall(pttn, str)

pttn = r'colou?red'
re.findall(pttn, str)

pttn = r'520*'
re.findall(pttn, str)

输出结果:

['google', 'gooogle', 'goooogle', 'goooooogle']

['google', 'gooogle', 'goooogle']

['coloured', 'colored']

['520', '52000', '5200000', '520000000', '520000000000']

或操作符: | 

或操作符 | 是所有操作符中优先级最低的,数量操作符的优先级比它高。

在 |  前后的原子,被数量操作符操作之后,才交给 | 操作;

在集合原子中(即,[] 内的原子)各个原子之间的关系,只有 “或” —— 相当于方括号中的每个原子之间都有一个被省略的 |。

注意:方括号的 | 不被当作特殊符号,而是被当作 | 这个符号本身。在方括号中的圆括号,也被当作圆括号 () 本身,而无分组含义。

匹配并捕获

捕获使用圆括号()。得到的匹配的值被暂存成一个带有索引的列表,第一个是$1,第二个是$2....我们可以在替换过程中使用$1...中所保存的值。

注意:在 Python 语言中调用 re 模块之后,在 re.sub() 中调用被匹配的值,用的索引方法是 \1\2…… 以此类推。

代码示例:

import re
str = 'The white dog wears a black hat.'
pttn = r'The (white|black) dog wears a (white|black) hat.'  #捕获 white 或 black
re.findall(pttn, str)   #并暂存为 \1 \2


repl = r'The \2 dog wears a \1 hat.'
re.sub(pttn, repl, str)

repl = r'The \1 dog wears a \1 hat.'
re.sub(pttn, repl, str)

输出:

[('white', 'black')]
'The black dog wears a white hat.'
'The white dog wears a white hat.'

非捕获匹配

有时,你并不想捕获圆括号中的内容,在那个地方使用括号的目的只是分组,而非捕获

这么做:在圆括号最开头加上?:——(?:.....)

代码示例:

import re
str = 'The white dog wears a black hat.'
pttn = r'The (?:black|white) dog wears a (white|black) hat.'
re.findall(pttn, str)  #只捕获了后面一处,只有一个值将来可以被引用

repl = r'The \1 dog wears a \1 hat.'  #捕获的值可以多次引用
re.sub(pttn, repl, str)
#返回值:
['black']
'The black dog wears a black hat.'

非捕获还有若干个操作符:

(?=pattern):正向肯定预查

正向肯定预查(look ahead positive assert),在任何匹配规则的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,Windows(?=95|98|NT|2000) 能匹配 Windows2000 中的 Windows,但不能匹配 Windows3.1 中的 Windows。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。

(?!pattern):

正向否定预查(negative assert),在任何不匹配规则的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如Windows(?!95|98|NT|2000) 能匹配 Windows3.1 中的 Windows,但不能匹配 Windows2000 中的 Windows。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。

(?<=pattern)

反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。例如,(?<=95|98|NT|2000)Windows 能匹配 2000Windows 中的 Windows,但不能匹配 3.1Windows 中的 Windows

(?<!pattern)

反向否定预查,与正向否定预查类似,只是方向相反。例如 (?<!95|98|NT|2000)Windows 能匹配 3.1Windows 中的 Windows,但不能匹配 2000Windows 中的 Windows

写在最后

自学编程的路好孤独啊,就是单纯有些孤独,有时候加了一些群,看着那些聊天内容,却又不想去问了,期待后来的同行朋友,哪天看到这篇文章,可以加我Q,咱们可以互相交流,也期待大佬能来带带我这个小白哈哈哈:635927730

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值