Python 正则表达式用法总结

401 阅读3分钟

写在前面

记录一下前阶段学的正则表达式(Regular Expression,一称RegExp、regex、RE),算是个复习+总结。我主要是从Python的Re库开始学正则的,不过正则的一些用法大同小异,学了一种就差不多明白了。想深入了解RE的话可以看下面的参考资料,写得很详细。

P.S. 前面几项都可以通用,只不过在Python中有内置的Re正则表达式库,里面的函数跟其他语言不太一样。

测试环境

Windows10 x64

Python 3.6.8

特殊字符

匹配单字符的字符

列出常用的单字符匹配以及对应表达的注记,感觉编程的东西对应英文会记得更牢些。

字符匹配项注记
·匹配任意1个字符(除特殊字符,如\n)-
\d匹配0-9中的任一数字字符,等价于[0-9]digit,单个数字
\D匹配任一非数字字符,等价于[^0-9]大写取反
\s匹配任一空白字符,如空格 、制表符(Tab)、换行符(\nspace,空格
\S匹配任一非空白字符-
\w匹配任一单词字符(可理解为变量命名规定的字符)
a-zA-Z0-9、下划线_,等价于[a-zA-z0-9_]
word,单词
\W匹配任一非单词字符,等价于[^a-zA-z0-9_]-
[]匹配方括号中列举出的任一字符-
[^ ]匹配除了括号内列举以外的所有字符-

表示匹配次数的字符

字符含义
{n}匹配字符恰好出现n次
{n,}匹配字符至少出现n次
{n,m}匹配字符出现n到m
*匹配字符出现0次或无限次(即字符出现任意次,.*表示匹配任意长度且除\n的字符串)
+匹配字符出现1次或无限次(即字符出现至少一次)
?匹配0次或1次的字符(多用于非贪婪模式)

定界字符

字符用法
\b匹配一个单词字符串的边界(boundary),即单词和空格间的位置
\B匹配非单词边界,即若某字串出现的单词字串未以空格分割,则不能匹配
^匹配字符串的第一个字符(首字符)
$匹配字符串的最后一个字符(末尾字符)

其他常用字符

字符用法
r" "Python中置于字符串之前用于返回原始(raw)字符串,在正则表达式编写中常用
\用于*?等特殊字符的转义
\n,\t匹配换行符、制表符,也可由\s进行匹配

Re库常用函数(方法)

# 导入内置库`re`
import re

re.compile()编译正则表达式

compile(pattern, flags=0)

用于生成一个正则表达式对象,方便正则表达式的迁移,供re.match()re.search()使用。

re.match()匹配字串

match(pattern, string, flags=0)

用于匹配以某字符串开头的字符串。通常与.group()联合使用,用于返回匹配到的字串,若匹配为空则报错:AttributeError: 'NoneType' object has no attribute 'group'

  • .group()返回字串

默认参数为0,表示返回所有匹配到的字符串;

参数为1表示返回()分组的第一个字符串,以此类推。

re.search()re.findall()查找字串

search(pattern, string, flags=0)

findall(pattern, string, flags=0)

单个查找与全局查找。search()match()的区别在于前者进行全局的查找,并返回首个匹配的结果,而后者仅进行从首个字符开始的查找匹配。

re.sub()替换字串

sub(pattern, repl, string, count=0, flags=0)

按匹配规则进行部分(或全部)字符串的替换,第二个参数(被替换成的字符)可以为函数。

re.split()分割字串

split(pattern, string, maxsplit=0, flags=0)

按匹配规则分割字符串,并返回一个列表。

贪婪模式与非贪婪模式

这块内容比较少,主要就是"?"的使用,在设置字符匹配次数的时候应尽量使用非贪婪模式,即在*+?的后面再加上一个?,构成*?+???,这样可以只匹配符合条件的最少字符,而不至于增加不必要的字符或是漏掉该匹配的字符。

{n,m}后面也可以加上?进入非贪婪模式。

\bigstar括号分组

分组是Python正则表达式的常用方法,不过要注意分组间的对应关系。

分组字符

字 符用法与备注
``分隔两侧的正则表达式
()将括号内的模式(字符)作为一个分组
\1-\9引用分组\i(i=1,2,,9)(i=1,\,2,\,\cdots,\,9)匹配到的字符串
(?P<name>)分组起别名(name),注意P要大写,引用时亦然
(?P=name)引用别名为name的分组匹配到的字符串,多用于匹配成对的HTML标签
(?<=abc)肯定性回顾断言,即若括号内容匹配,则.group()返回括号后面匹配到的字符
(?<!abc)否定性回顾断言,即若括号内容不匹配,则.group()返回括号后面匹配到的字符
(?=abc)肯定性前瞻断言,即若括号内容匹配,则.group()返回括号前面匹配到的字符
(?!abc)否定性前瞻断言,即若括号内容不匹配,则.group()返回括号前面匹配到的字符

注意: 上面的后四个匹配断言仅用于.search().findall(),不可用于.match().

示例

In [1]: import re
    
In [2]: re.search(r"(?<=abc)\d+", r"abc123").group()
Out[2]: '123'
    
In [3]: re.search(r"(?<!abc)\d+", r"bca123").group()
Out[3]: '123'
    
In [4]: re.search(r"\d+(?=abc)", r"123abc").group()
Out[4]: '123'
    
In [5]: re.search(r"\d+(?!abc)", r"123bca").group()
Out[5]: '123'
    
# 设定分组别名后可以以字典形式展示匹配到的分组

In [6]: re.match(r"(?P<group1>[a-z]+)(?P<group2>\d+)", r"bca123").groupdict() 
Out[6]: {'group1': 'bca', 'group2': '123'}
    
In [7]: re.match(r"(?P<group1>[a-z]+)(?P<group2>\d+)", r"bca123").group("group1")
Out[7]: 'bca'
    
In [8]: re.match(r"<(?P<group1>[a-z]*?)>(.*?)</(?P=group1)>", r"<div>你好世界!</div>").group()
Out[8]: '<div>你好世界!</div>'

In [9]: re.match(r"<(?P<group1>[a-z]*?)>(.*?)</(?P=group1)>", r"<div>你好世界!</div>").group(2)
Out[9]: '你好世界!'
    
# 使用"反斜杠+数字"进行分组的引用更为方便

In [10]: re.match(r"<([a-z]*?)>(.*?)</\1>", r"<div>你好世界!</div>").group()
Out[10]: '<div>你好世界!</div>'

In [11]: re.match(r"<([a-z]*?)>(.*?)</\1>", r"<div>你好世界!</div>").group(2)
Out[11]: '你好世界!'

一些技巧

正难则反

需要匹配的字符串规则不好找时候可以选择容易找到规则的其他字符,再进行分组处理得到需要的字符。

例如,网址“docs.python.org/3/library/r…

In [1]: import re

In [2]: s = r"https://docs.python.org/3/library/re.html"

In [3]: re.sub(r"(https://.+?/).+", lambda x:x.group(1), s)
Out[3]: 'https://docs.python.org/'

函数活用

有时候解决同一个问题可以有多种思路,例如提取单词,可以采用空格分割或者全局查找两种方法,所得到的结果相同。

In [4]: s = r"I have a dream"

In [5]: re.split(r" ", s)
Out[5]: ['I', 'have', 'a', 'dream']

In [6]: re.findall(r"\b[a-zA-z]+?\b", s)
Out[6]: ['I', 'have', 'a', 'dream']

主要参考

[1] re — Regular expression operations.

[2] 菜鸟教程:Python 正则表达式.