Python-正则表达式
1.为什么要用正则表达式
“知道[正则表达式]可能意味着用 3 步解决一个问题,而不是用 3000 步。如果
你是一个技术怪侠,别忘了你用几次击键就能解决的问题,其他人需要数天的烦琐
工作才能解决,而且他们容易犯错。”
2.使用方法
简单举例:
phoneNumRegex = re.compile(r'\d{3}-\d{3}-\d{4}') # 创建Regex对象 mo = phoneNumRegex.search("my number is 415-555-4242") # print('phone number found:' + mo.group())
变量名 mo 是一个通用的名称,用于 Match 对象。
这里,我们将期待的模式传递给 re.compile(),并将得到的 Regex 对象保存phoneNumRegex 中。然后我们在 phoneNumRegex 上调用 search(),向它传入想查找的字符串。查找的结果保存在变量 mo 中。在这个例子里,我们知道模式会在这个符串中找到,所以我们知道会返回一个 Match 对象。知道 mo 包含一个 Match 对象,而不是空值 None,我们就可以在 mo 变量上调用 group(),返回匹配的结果。将 mo.group()写在打印语句中,显示出完整的匹配,即 415-555-4242。
3. 正则表达式匹配更多模式
3.1 利用括号分组
phone = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') mo = phone.search('My number is 422-333-3333')
print(mo.group(0))
print(mo.group(1))
print(mo.group(2))
假定想要将区号从电话号码中分离。添加括号将在正则表达式中创建“分组”:
(\d\d\d)-(\d\d\d-\d\d\d\d)。然后可以使用 group()匹配对象方法,从一个分组中获取匹
配的文本。
正则表达式字符串中的第一对括号是第 1 组。第二对括号是第 2 组。向 group()
匹配对象方法传入整数 1 或 2,就可以取得匹配文本的不同部分。向 group()方法传
入 0 或不传入参数,将返回整个匹配的文本。
如果想要一次就获取所有的分组,请使用 groups()方法,注意函数名的复数形式。
mo.groups() ('415', '555-4242') >>> areaCode, mainNumber = mo.groups() >>> print(areaCode) 415 >>> print(mainNumber) 555-4242
如果匹配的文本中特殊字符,例如括号,就需要使用转义符(\)来将特殊符号进行转义来匹配文本。
phone1 = re.compile(r'(\(\d\d\d\)) (\d\d\d-\d\d\d\d)') mo = phone1.search('my phone number is (415) 555-3333') print(mo.group())
3.2 用管道匹配多个分组
字符 “|” 称为“管道”。希望匹配许多表达式中的一个时,就可以使用它。
name = re.compile(r'haha|xixi') mo = name.search("I have a xixi and a haha") print(mo.group())
如果haha 和 xixi 都出现在被查找的字符串中,第一次出现的匹配文本,将作为Match对象返回。
假设你希望匹配‘xiaogang’,'xiaofang','xiaoming'中的一个。因为所有这些都是以xiao开头,所以如果能够只指定一次前缀,就很方便。这可以通过括号实现。
name = re.compile(r"xiao(gang|ming|fang)") mo = name.search("xiaogang,xiaofang,xixi,momo") print(mo.group())
方法调用 mo.group()返回了完全匹配的文本'Batmobile',而 mo.group(1)只是返
回第一个括号分组内匹配的文本'mobile'。通过使用管道字符和分组括号,可以指定
几种可选的模式,让正则表达式去匹配。
如果需要匹配真正的管道字符,就用倒斜杠转义,即\|
3.3 用问号实现可选匹配
有时候,想匹配的模式是可选的。就是说,无论这段文本在不在,正则表达式都会认为匹配。字符?表名它1前面的分组在这个模式中是可选的。
格式: (ha)?
name = re.compile(r"ka(la)?ka") mo = name.search('kaka,kalaka') print(mo.group())
3.4 用星号匹配零次或多次
*(称为星号)意味着“匹配零次或多次”,即星号之前的分组,可以在文本中出现任意次。它可以完全不存在,或一次又一次地重复
name = re.compile(r"(ha)*xiaojiao") mo = name.search("I have a hahahahahaxiaojiao") print(mo.group())
3.5 用加号匹配一次或多次
*意味着“匹配零次或多次”,+(加号)则意味着“匹配一次或多次”。星号不要求
分组出现在匹配的字符串中,但加号不同,加号前面的分组必须“至少出现一次”。这不
是可选的。
name = re.compile(r"(ao)+jiao") mo = name.search("I have a aojiao") # 一条 mo = name.search("I have a aoaojiao") #多条 print(mo.group())
如果需要匹配真正的加号字符,在加号前面加上倒斜杠实现转义:\+。
3.6 用花括号匹配特定次数
如果想要一个分组重复特定次数,就在正则表达式中该分组的后面,跟上花括
号包围的数字。例如,正则表达式(Ha){3}将匹配字符串'HaHaHa',但不会匹配'HaHa',因为后者只重复了(Ha)分组两次。
除了一个数字,还可以指定一个范围,即在花括号中写下一个最小值、一个逗号和
一个最大值。例如,正则表达式(Ha){3,5}将配'HaHaHa'、'HaHaHaHa'和'HaHaHaHaHa'。
也可以不写花括号中的第一个或第二个数字,不限定最小值或最大值。Ha){3,}将匹配 3 次或更多次实例,(Ha){,5}将匹配 0 到 5 次实例。如果有五个ha则返回五个而不是三个
name = re.compile(r"(ha){3}") mo = name.search("hahaha, I win") print(mo.group())
3.7 贪心和非贪心匹配
Python 的正则表达式默认是“贪心”的,这表示在有二义的情况下,它们会尽
可能匹配最长的字符串。花括号的“非贪心”版本匹配尽可能最短的字符串,即在
结束的花括号后跟着一个问号。
name = re.compile(r"(ha){3,5}") mo = name.search("hahahahahaha, I win") print(mo.group()) name = re.compile(r"(ha){3,5}?") mo = name.search("hahahahahaha, I win") print(mo.group())
3.8 findall()方法
除了search方法外,Regex对象也有一个findall()方法。search()将返回一个Match
对象,包含被查找字符串中的“第一次”匹配的文本,而 findall()方法将返回一组
字符串,包含被查找字符串中的所有匹配。
num = re.compile(r"\d\d\d") mo = num.findall("433,324,This is a box") print(mo)
另一方面,findall()不是返回一个 Match 对象,而是返回一个字符串列表,只要
在正则表达式中没有分组。列表中的每个字符串都是一段被查找的文本,它匹配该
正则表达式。
如果在正则表达式中有分组,那么 findall 将返回元组的列表。每个元组表示一个找
到的匹配,其中的项就是正则表达式中每个分组的匹配字符串。
name = re.compile('(\d\d) (\d\d)') mo = name.findall("42 33 34 44, xixixi") print(mo) [('42', '33'), ('34', '44')]
作为 findall()方法的返回结果的总结,请记住下面两点:
1.如果调用在一个没有分组的正则表达式上,例如\d\d\d-\d\d\d-\d\d\d\d,方法
findall()将返回一个匹配字符串的列表,例如['415-555-9999', '212-555-0000']。
2.如果调用在一个有分组的正则表达式上,例如(\d\d\d)-(\d\d\d)-(\d\d\d\d),方
法 findall()将返回一个字符串的元组的列表(每个分组对应一个字符串),例如[('415',
'555', '1122'), ('212', '555', '0000')]
3.9 字符分类
缩写字符分类 | 表示 |
\d | 0到9的任何数字 |
\D | 除0到9的数字以外的任何字符 |
\w | 任何字母、数字或下划线字符(可以认为是匹配“单词”字符) |
\W | 除字母、数字和下划线以外的任何字符 |
\s | 空格、制表符或换行符(可以认为是匹配“空白”字符) |
\S | 除空格、制表符和换行符以外的任何字符 |
name = re.compile(r'\d+\s\w') mo = name.search("a,2 b") print(mo.group())
2 b
正则表达式\d+\s\w+匹配的文本有一个或多个数字(\d+),接下来是一个空白字
符(\s),接下来是一个或多个字母/数字/下划线字符(\w+)。findall()方法将返回所有匹
配该正则表达式的字符串,放在一个列表中
3.10 建立自己的字符分类
匹配 | 含义 |
[aeiouAEIOU] | 匹配所有元音字符,无论大小写 |
[a-zA-Z0-9] | 匹配所有小写字母、大写字母和数字 |
[^aeiouAEIOU] | 非字符类。非字符类将匹配不在这个字符类中的所有字符 |
请注意,在方括号内,普通的正则表达式符号不会被解释。这意味着,你不需
要前面加上倒斜杠转义.、*、?或()字符。
name = re.compile(r'[a-zA-Z0-9]') mo = name.findall("I have a dream") print(mo) ['I', 'h', 'a', 'v', 'e', 'a', 'd', 'r', 'e', 'a', 'm']
3.11 插入字符和美元字符
可以在正则表达式的开始处使用插入符号(^),表明匹配必须发生在被查找文
本开始处。类似地,可以再正则表达式的末尾加上美元符号($),表示该字符串必
须以这个正则表达式的模式结束。可以同时使用^和$,表明整个字符串必须匹配该
模式,也就是说,只匹配该字符串的某个子集是不够的。
re.compile(r'^\d+$')
3.12 通配字符
在正则表达式中,.(句点)字符称为“通配符”。它匹配除了换行之外的所有
字符。
name = re.compile(r'.cat') mo = name.findall('xcat,ycat,jcat') print(mo) ['xcat', 'ycat', 'jcat']
有时候想要匹配所有字符串。例如,假定想要匹配字符串'First Name:',接下来
是任意文本,接下来是'Last Name:',然后又是任意文本。可以用点-星(.*)表示“任
意文本”。回忆一下,句点字符表示“除换行外所有单个字符”,星号字符表示“前
面字符出现零次或多次”。
name = re.compile(r'First Name:(.*) Last Name:(.*)') mo = name.search('First Name: AI Last Name:Sweigart') print(mo.group()) First Name: AI Last Name:Sweigart