记录一下前阶段学的正则表达式(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 )、换行符(\n ) |
space,空格 |
\S |
匹配任一非空白字符 | - |
\w |
匹配任一单词字符(可理解为变量命名规定的字符) 即 a-z 、A-Z 、0-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}
后面也可以加上?
进入非贪婪模式。
分组是Python正则表达式的常用方法,不过要注意分组间的对应关系。
字 符 | 用法与备注 |
---|---|
| |
分隔两侧的正则表达式 |
() |
将括号内的模式(字符)作为一个分组 |
\1 -\9 |
引用分组\i ( i = 1 , 2 , ⋯ , 9 ) (i=1,\,2,\,\cdots,\,9) (i=1,2,⋯,9)匹配到的字符串 |
(?P |
分组起别名(name),注意P 要大写,引用时亦然 |
(?P=name) |
引用别名为name的分组匹配到的字符串,多用于匹配成对的HTML标签 |
(?<=abc) |
肯定性回顾断言,即若括号内容匹配,则.group() 返回括号后面匹配到的字符 |
(? |
否定性回顾断言,即若括号内容不匹配,则.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"(?, 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[a-z]+)(?P\d+)" , r"bca123").groupdict()
Out[6]: {'group1': 'bca', 'group2': '123'}
In [7]: re.match(r"(?P[a-z]+)(?P\d+)" , r"bca123").group("group1")
Out[7]: 'bca'
In [8]: re.match(r"<(?P[a-z]*?)>(.*?)(?P=group1)>" , r"你好世界!").group()
Out[8]: '你好世界!'
In [9]: re.match(r"<(?P[a-z]*?)>(.*?)(?P=group1)>" , r"你好世界!").group(2)
Out[9]: '你好世界!'
# 使用"反斜杠+数字"进行分组的引用更为方便
In [10]: re.match(r"<([a-z]*?)>(.*?)\1>", r"你好世界!").group()
Out[10]: '你好世界!'
In [11]: re.match(r"<([a-z]*?)>(.*?)\1>", r"你好世界!").group(2)
Out[11]: '你好世界!'
需要匹配的字符串规则不好找时候可以选择容易找到规则的其他字符,再进行分组处理得到需要的字符。
例如,网址“https://docs.python.org/3/library/re.html”需要替换成“https://docs.python.org/”,在进行匹配的时候由于待替换的部分其规律无法找到,所以采取如下方法进行替换:
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 正则表达式.