本文学自:李笑来老师的《自学是门手艺》
一、基本概念
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_letters
和 string.digits
以及 _
2.以下字符都有特殊含义(后面详细说):
\ + * ? . - ^ $ | ( ) { } [ ] < >
当你在写Regex的时候,如果需要搜索的字符不是本义字符,而是以上这些特殊字符时,建议都直接加上转义符号 \ 来表示。你想搜索 ' ,那就写 \' ,或者你想搜 # ,那就写 \# (这里#号前面不一定非要加 \ 因为它不是Regex的特殊符号,所以转义符可有可无。)
集合原子
集合原子还是原子
1.标示方法: [ ]
[abc] 意思是说 a or b or c ,即abc中的任意一个字符。
比如,
beg[iau]n
能够代表begin
、began
,以及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
是 flipn
是 new liner
是 returnt
是 tabv
是 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