正则表达式对象
1. re.RegexObject
Function name | Description |
---|---|
re.compile() | 返回 RegexObject 对象 |
2. re.MatchObject
Function name | Description |
---|---|
group() | 返回被 RE 匹配的字符串。 |
start() | 返回匹配开始的位置 |
end() | 返回匹配结束的位置 |
span() | 返回一个元组包含匹配 (开始,结束) 的位置 |
匹配模式
比如忽略大小写,多行模式等,具体参数为:
param | Description |
---|---|
re.I | 忽略大小写 |
re.L | 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境 |
re.M | 多行模式 |
re.S | 即为 . 并且包括换行符在内的任意字符(. 不包括换行符) |
re.U | 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库 |
re.X | 为了增加可读性,忽略空格和 # 后面的注释 |
RegEx 函数
Function | Description |
---|---|
compile | 编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用 |
match | 匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败 |
search | Returns a Match object if there is a match anywhere in the string. If fail to match, return None |
findall | Returns a list containing all matches |
split | Returns a list where the string has been split at each match |
sub | 替换字符串中的匹配项 |
re.match与re.search的区别
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
实例
import re
basic_str = "The rain in China"
basic_findall_res = re.findall("in", basic_str) # match "in", return Match Object
basic_search_res = re.search("\s", basic_str) # match white space
basic_split_res = re.split("\s", basic_str, 2) # Split at each white-space character = str.split(" ")
basic_sub_res = re.sub("\s", "9", basic_str) # Replace every white-space character with the number 9
print(basic_findall_res)
print(basic_search_res, basic_search_res.group(), basic_search_res.start(), basic_search_res.end())
print(basic_split_res)
print(basic_sub_res)
## 结果:
# ['in', 'in', 'in']
# <re.Match object; span=(3, 4), match=' '> 3 4
# ['The', 'rain', 'in China']
# The9rain9in9China
## compiler
pattern = re.compile(r'\d+') # 用于匹配至少一个数字
res = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置(index=3)开始匹配,正好匹配
print(res) # 结果:<re.Match object; span=(3, 5), match='12'>
## groups
reg = r"(\w+),?"
str = "aabb,xxx,yysin,ienif"
matchResult = re.finditer(reg, str, re.I)
for result in matchResult:
result.group()#匹配结果
result.groups()#匹配结果当中,捕获结果依次组成的元组对象
if len(result.groups()) > 0:
for g in result.groups():
print(g)
## 结果:
# aabb
# xxx
# yysin
# ienif
1. 基础匹配
import re
basic_str = "The rain in China"
basic_search_res = re.search("\s", basic_str) # match white space
print(basic_findall_res) # res:['in', 'in', 'in']
2. 元字符
元字符 | 描述 |
---|---|
. | 句号匹配任意单个字符除了换行符。 |
[ ] | 字符种类。匹配方括号内的任意字符。 |
[^ ] | 否定的字符种类。匹配除了方括号里的任意字符 |
* | 匹配>=0个重复的在*号之前的字符。 |
+ | 匹配>=1个重复的+号前的字符。 |
? | 标记?之前的字符为可选. |
{n,m} | 匹配num个大括号之间的字符 (n <= num <= m). |
(xyz) | 字符集,匹配与 xyz 完全相等的字符串. |
| | 或运算符,匹配符号前或后的字符. |
\ | 转义字符,用于匹配一些保留的字符 [ ] ( ) { } . * + ? ^ $ \ |
^ | 从开始行开始匹配. |
$ | 从末端开始匹配. |
转码特殊字符
反斜线 \
在表达式中用于转码紧跟其后的字符。用于指定 { } [ ] / \ + * . $ ^ | ?
这些特殊字符。如果想要匹配这些特殊字符则要在其前面加上反斜线\
。
import re
s = "The rain in China"
s2 = "pi is nearly equal to 3.4159265359"
print(re.findall(r".h", s)) # ['Th', 'Ch']
print(re.findall(r"[rn]a", s)) # ['Th', 'Ch']
print(re.findall(r"h[^in]", s)) # ['he']
print(re.findall(r"[a-zA-Z]*i", s)) # ['rai', 'i', 'Chi']
print(re.findall(r"\s*rain\s*", s)) # [' rain ']
print(re.findall(r"C.+n", s)) # ['Chin']
print(re.findall(r"[a-z]?in", s)) # ['ain', 'in', 'hin']
print(re.findall(r"(H|h)in|The", s)) # ['', 'h'] . 匹配hin,Hin 或者The
# ^ 指定开头,$ 指定结尾
print(re.findall(r"^[Tt]he", s)) # ['The'] . 配置以The或者the开头的字符串
print(re.findall(r"^[Tt]he.*na$", s)) # ['The'] . 配置以The或者the开头,以na结尾的字符串
print(re.findall(r"[0-9]{2,4}", s2)) # ['4159', '2653', '59']
print(re.findall(r"[0-9]{2,}", s2)) # ['4159265359'] . 至少匹配两位0-9的数字
print(re.findall(r"[0-9\.]{2,4}", s2)) # ['3.41', '5926', '5359']
3. 简写字符集
简写 | 描述 |
---|---|
. | 除换行符外的所有字符 |
\w | 匹配所有字母数字,等同于[a-zA-Z0-9_] |
\W | 匹配所有非字母数字,即符号,等同于: [^\w] |
\d | 匹配数字: [0-9] |
\D | 匹配非数字: [^\d] |
\s | 匹配所有空格字符,等同于:[\t\n\f\r\p{Z}] |
\S | 匹配所有非空格字符:[^\s] |
\f | 匹配一个换页符 |
\n | 匹配一个换行符 |
\r | 匹配一个回车符 |
\t | 匹配一个制表符 |
\v | 匹配一个垂直制表符 |
\p | 匹配CR/LF (等同于\r\n ),用来匹配 DOS 行终止符 |
4.零宽度断言(前后预查)
先行断言和后发断言都属于非捕获簇(不捕获文本 ,也不针对组合计进行计数)。 先行断言用于判断所匹配的格式是否在另一个确定的格式之前,匹配结果不包含该确定格式(仅作为约束)。
例如,我们想要获得所有跟在 $ 符号后的数字,我们可以使用正后发断言 (?<=\$)[0-9\.]*
。 这个表达式匹配 $ 开头,之后跟着 0,1,2,3,4,5,6,7,8,9,. 这些字符可以出现大于等于 0 次。
零宽度断言如下:
符号 | 描述 |
---|---|
?= | 正先行断言-存在 |
?! | 负先行断言-排除 |
?<= | 正后发断言-存在 |
?<! | 负后发断言-排除 |
4.1 ?=… 正先行断言
?=… 正先行断言,表示第一部分表达式之后必须跟着 ?=…定义的表达式。
返回结果只包含满足匹配条件的第一部分表达式。 定义一个正先行断言要使用 ()。在括号内部使用一个问号和等号: (?=…)。
正先行断言的内容写在括号中的等号后面。 例如,表达式 (T|t)he(?=\sfat) 匹配 The 和 the,在括号中我们又定义了正先行断言 (?=\sfat) ,即 The 和 the 后面紧跟着 (空格)fat。
“(T|t)he(?=\sfat)” => The
fat cat sat on the mat.
在线练习
4.2 ?!.. 负先行断言
负先行断言 ?! 用于筛选所有匹配结果,筛选条件为 其后不跟随着断言中定义的格式。 正先行断言 定义和 负先行断言 一样,区别就是 = 替换成 ! 也就是 (?!..)。
表达式 (T|t)he(?!\sfat) 匹配 The 和 the,且其后不跟着 (空格)fat。
“(T|t)he(?!\sfat)” => The fat cat sat on the
mat.
在线练习
4.3 ?<= … 正后发断言
正后发断言 记作(?<=…) 用于筛选所有匹配结果,筛选条件为 其前跟随着断言中定义的格式。 例如,表达式 (?<=(T|t)he\s)(fat|mat) 匹配 fat 和 mat,且其前跟着 The 或 the。
“(?<=(T|t)he\s)(fat|mat)” => The fat
cat sat on the mat
.
在线练习
4.4 ?<!.. 负后发断言
负后发断言 记作 (?<!..) 用于筛选所有匹配结果,筛选条件为 其前不跟随着断言中定义的格式。 例如,表达式 (?<!(T|t)he\s)(cat) 匹配 cat,且其前不跟着 The 或 the。
“(?<!(T|t)he\s)(cat)” => The cat sat on cat
.
5. 标志
标志也叫模式修正符,因为它可以用来修改表达式的搜索结果。这些标志可以任意的组合使用,它也是整个正则表达式的一部分。
标志 | 描述 |
---|---|
i | 忽略大小写(等同于python中的re.I )。 |
g | 全局搜索 |
m | 多行修饰符:锚点元字符^ $ 工作范围在每行的起始 (等同于python中的re.M ) |
s = "The fat cat sat on the mat."
print(re.findall(r"the", s, re.I)) # ['The', 'the']
6. 贪婪匹配与惰性匹配(Greedy vs lazy matching)
正则表达式默认采用贪婪匹配模式,在该模式下意味着会匹配尽可能长的子串。我们可以使用 ?
将贪婪匹配模式转化为惰性匹配模式。
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复
代码/语法 | 说明 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
“/(.at)/" => The fat cat sat on the mat
.
"/(.?at)/” => The fat
cat sat on the mat.
高级技巧
1. 分支条件
正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|
把不同的规则分隔开。看例子:
0\d{2}-\d{8}|0\d{3}-\d{7}
这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678)
,一种是4位区号,7位本地号(0376-2233445)
。
n = "010-12345678"
n2 = "0376-2233445"
print(re.findall("0\d{2}-\d{8}|0\d{3}-\d{7}", n))
print(re.findall("0\d{2}-\d{8}|0\d{3}-\d{7}", n2))
## 结果:
# ['010-12345678']
# ['0376-2233445']
\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}
这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。
\d{5}-\d{4}|\d{5}
这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}
的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。
2. 分组
我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。
(\d{1,3}\.){3}\d{1,3}
是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}
匹配1到3位的数字,(\d{1,3}.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。
不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
。
理解这个表达式的关键是理解2[0-4]\d | 25[0-5] | [01]?\d\d?
,这里我就不细说了,你自己应该能分析得出来它的意义。