在这里记录一下最近学习到的Python正则表达是的知识点
正则表达式是什么?
正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
通俗来讲,可以把正则表达式理解为类似于一门计算机语言,不过它是用来描述符合某些规则的字符串的。
基础
- Python 中正则表达式模块是
re,只要import re就好 最简单的正则表达式就是我们想要匹配的字符串,比如
1
2
3
4
5>>> import re
>>> key = 'javapythonc++'
>>> reg = 'python'
>>> re.compile(reg).findall(key)
['python']
re模块的常用用法
匹配(match)
判断给定的字符串是否符合正则表达式的描述。
如果match,就会返回一个_sre.SRE_Match对象,否则返回空1
2
3
4
5>>> import re
>>> key = 'python'
>>> reg = 'python'
>>> re.compile(reg).match(key)
<_sre.SRE_Match object; span=(0, 6), match='python'>查找
在给定字符串中查找符合正则表达式的描述的子字符串。search判断是否能找到符合标准的子字符串findall找出所有的符合标准的子字符串
1
2
3
4
5
6
7
>>> import re
>>> key = 'javapythonjavapython'
>>> reg = 'python'
>>> re.compile(reg).search(key) # 是否能找到'python'
<_sre.SRE_Match object; span=(4, 10), match='python'>
>>> re.compile(reg).findall(key) # 找到所有的 'python'
['python', 'python']
常用正则表达式的元字符及其作用
| 元字符 | 说明 | |
|---|---|---|
| . | 代表任意字符 | |
| \ | 逻辑或操作符 | |
| [ ] | 匹配内部的任一字符或子表达式 | |
| [\^] | 对字符集和取非 | |
| - | 定义一个区间 | |
| \ | 对下一字符取非(通常是普通变特殊,特殊变普通) | |
| * | 匹配前面的字符或者子表达式0次或多次 | |
| *? | 惰性匹配上一个 | |
| + | 匹配前一个字符或子表达式一次或多次 | |
| +? | 惰性匹配上一个 | |
| ? | 匹配前一个字符或子表达式0次或1次重复 | |
| {n} | 匹配前一个字符或子表达式 | |
| {m,n} | 匹配前一个字符或子表达式至少m次至多n次 | |
| {n,} | 匹配前一个字符或者子表达式至少n次 | |
| {n,}? | 前一个的惰性匹配 | |
| ^ | 匹配字符串的开头 | |
| \A | 匹配字符串开头 | |
| $ | 匹配字符串结束 | |
| [\b] | 退格字符 | |
| \c | 匹配一个控制字符 | |
| \d | 匹配任意数字 | |
| \D | 匹配数字以外的字符 | |
| \t | 匹配制表符 | |
| \w | 匹配任意数字字母下划线 | |
| \W | 不匹配数字字母下划线 |
特别的,我们要记住\能够把普通的字符变特殊,特殊字符变普通。
比如.代表匹配所有字符, \.则代表单纯的一个点d代表单纯的d这个字母,\d则代表0-9的数字
小括号(),中括号[],大括号{}的区别
小括号()
小括号形成子表达式,比如
1 | import re |
什么都匹配不到,因为(0-9)代表的是0-9这个字符串
中括号[]
中括号匹配字符组内的字符,比如
1 | [a-zA-Z0-9] |
这个正则表达式实际上只匹配一个字符,但是这个字符可以是大小写字母或者数字。
1 | >>> import re |
如果加上+号,就代表匹配一个或多个
1 | >>> reg = '[a-zA-Z0-9]+' |
大括号{}
大括号代表匹配次数,匹配在它之前表达式匹配出来的元素出现的次数。
{n}出现n次{n,}匹配最少出现n次{n,m}匹配最少出现n次,最多出现m次
进阶用法
向前向后查找
简单来说,就是你要匹配的字符串是XX,但是必须满足形式是AXXB这样的字符串,那么正则表达式就可以写成这样
1 | p=r"(?<=A)XX(?=B)" |
?<=代表字符串前必须要有A, 即前缀要求?=代表字符串后必须要有B,即后缀要求
本质上来说,向前查找和向后查找其实是匹配整个字符串,即AXXB,但返回时仅仅返回一个XX。也就是说,如果你愿意,完全可以避开向前向后查找的方式,直接匹配带有前后缀的字符串,然后做字符串切片处理。
回溯引用
比如我们要匹配形如<h1>hello world</h1>这样的字符串,同时不仅限于<h1>,而是获取<h1>到<h6>的。
那我们就可以写成
1 | r"<h[1-6]>.*?</h[1-6]>" |
但是如果遇到像这样的特殊情况,就不好玩了
1 | <h1>hello world</h3> |
为了保证hello world前后都是一样的,我们可以用到回溯引用,就像这样
1 | r"<h([1-6])>.*?</h\1>" |
看到\1了吗?原本那个位置应该是[1-6],但是我们写的是\1,我们之前说过,转义符\干的活就是把特殊的字符转成一般的字符,把一般的字符转成特殊字符。普普通通的数字1被转移成什么了呢?在这里1表示第一个子表达式,也就是说,它是动态的,是随着前面第一个子表达式的匹配到的东西而变化的。比方说前面的子表达式内是[1-6],在实际字符串中找到了1,那么后面的\1就是1,如果前面的子表达式在实际字符串中找到了2,那么后面的\1就是2。
类似的,\2,\3,….就代表第二个第三个子表达式。
所以回溯引用是正则表达式内的一个“动态”的正则表达式,让你根据实际的情况变化进行匹配。