正则表达式
正则表达式是什么
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。正则表达式通常被用来检索、替换那些匹配某个模式的文本。
来点通俗易懂的,比如如下一个字符串:
data = '他的电话号码是:123456789'
从这里取出数字的这一本分就可以用正则来完成,利用他自身构造一种规则,取出特定的内容。如下代码:
>>> import re
>>> data = '他的电话号码是:123456789'
>>> a =re.search(r'([0-9].*)', data).group(1)
>>> print(a)
123456789
干货来了
匹配规则(重点)
模式 | 描述 |
---|---|
\w | 匹配字母、数字、以及下划线 |
\W | 匹配不是字母、数字、以及下划线的字符 |
\s | 匹配任意空白字符,等价于[\t\n\r\f] |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于[0-9] |
\D | 匹配任意非数字的字符 |
\A | 匹配字符串开头 |
\z | 匹配字符串的结尾,如果存在换行,同时还会匹配换行符 |
\Z | 匹配字符串的结尾,如果存在换行,职匹配到换行前的结束字符串 |
\G | 匹配最后匹配完成的位置 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
^ | 匹配一行字符串的开头 |
$ | 匹配一行字符串的结尾 |
. | 匹配任意字符,除了换行符 |
* | 匹配0个或者多个表达式 |
+ | 匹配1个或者多个表达式 |
[…] | 用来表示一组字符,单独列出,比如[amk]匹配a、m或者k |
[^…] | 不在[ ]中的的字符,比如[^abc]匹配除了a,b,c之外的字符 |
? | 匹配0或者1个前面的正则表达式,非贪婪方式 |
{n} | 精确匹配n个前面的正则表达式 |
{n,m} | 匹配n到m次由前面正则表达式定义的片段,贪婪方式 |
a|b | 匹配a或者b |
() | 匹配括号内的表达式,也表示一个组 |
匹配方法
- match(),search()
match函数会尝试从字符串的起始位置匹配正则表达式,如果匹配,就返回成功的结果,如果失败,返回None
search函数扫描整个字符串,如果匹配,就返回第一个匹配成功的结果,如果失败,返回None
两个函数都是需要两个参数,第一个是正则表达式,第二个是要过滤的文本
>>> data = 'hello jingge'
>>> a = re.match(r'hello', data) # 从起始位置匹配hello成功
>>>> b = re.match(r'jing', data) # 从起始位置匹配jing失败
>>>> c = re.match('[a-z]*',data) # 起始位置匹配任意小写字母0个或者多个
>>>> print(a)
<re.Match object; span=(0, 5), match='hello'>
>>> print(b)
None
>>> print(c)
<re.Match object; span=(0, 5), match='hello'>
>>> print(a.group()) # 利用group()方法输出匹配到的内容
hello
p匹配的内容用group()来获取,也可以用group(1)获取,通过以下代码体验一下
import re
data = '他的电话号码是:123456789,邮箱是jing@.com'
a = re.search(r'[0-9]+', data)
b = re.search(r'邮箱是(.*)', data)
print(b.group()) # 输出结果:邮箱是jing@.com
print(b.group(1)) # 输出结果:jing@.com
由此可见,单用group()会输出所有匹配到的内容,如果只要第一个括号里边的就用group(1),第二个括号就用group(2),以此类推
- findall()
前边说了,search()函数会查找整个字符串并返回第一个匹配成功的结果,也就是说有局限性,只会返回一个结果,findall函数就会返回所有的匹配成功的内容,结果以列表的形式返回。
使用方法同上。
import re
data = 'a的电话号码是:123456789,邮箱是jing@.com,b的电话号码是:105092000,邮箱是shui@.com,c的电话号码是:2019329,邮箱是jmaotai@.com'
a = re.search(r'[0-9]+', data)
b = re.findall(r'[0-9]+', data)
print(a.group())
print(b)
结果是:
123456789
[‘123456789’, ‘105092000’, ‘2019329’]
可以明显看出区别,findall函数不需要用group来获取内容,就可以以列表形式返回匹配内容
- sub()
sub函数可以用来修改字符串内容,如果要修改多出利用字符串的replace()替换显然很麻烦,这时就可以用sub函数完成。
函数需要三个参数,第一个是正则表达式,第二个是替换后的内容,第三个是要过滤的字符串。
如下是利用sub函数把数字去掉
>>> data = '123456nihao,77777beijing!'
>>> print(re.sub(r'\d+', '', data))
nihao,beijing!
- compile()
这个函数和前几个不同,它不是用来匹配内容的,它是用来编译正则字符串,把正则字符串编译成正则表达式对象,方便以后复用。
上代码体验一下
>>> data1 = '2019-3-29 22:00'
>>> data2 = '2019-3-30 22:00'
>>> pattern = re.compile(r'(.*)\s')
>>> a = re.search(pattern, data1)
>>> b = re.search(r'(.+)\s', data1)
>>> c = re.search(pattern, data2)
>>> print(a.group())
2019-3-29
>>> print(b.group())
2019-3-29
>>> print(c.group())
2019-3-30
>>>
可以看到,使用compile()编译后的效果和之前用的方式一样,而且编译后还可以方便以后的复用。
补充(也是重点)
通用匹配
有一个万用的匹配公式,也就是任何东西都可以匹配,除了换行符,那就是.(点星),.(点)是匹配任意字符,除了换行符,(星)是匹配0个或者多个表达式,加在一起就万能喽。
贪婪与非贪婪
贪婪与非贪婪的区别就是,在使用.*通用匹配时,贪婪匹配会尽可能的匹配更多的内容,非贪婪匹配是尽可能的匹配少的内容,看一段代码
>>> data = 'please call me 123 after 3 minutes'
>>> a = re.search(r'.*(\d+).*',data)
>>> b = re.search(r'.*?(\d+).*',data)
>>> print(a.group(1))
3
>>> print(b.group(1))
123
a得到的过滤内容是贪婪匹配,前边的. *(点星)匹配到了尽可能多的内容,所以只有一个3输出,而b在*后边加上了一个?,由上文的匹配规则可知道,?是匹配0或者1个前面的正则表达式,非贪婪方式,所以*匹配了尽可能少的字符,这才让(\d+)匹配完全。
修饰符
模式 | 描述 |
---|---|
re.I(大写的i) | 使匹配对大小写不敏感,也就是忽略大小写 |
re.L | 做本地化识别匹配 |
re.M | 多行匹配,影响^和$ |
re.S | 使.(点)匹配包括换行在内的所有字符 |
修饰符加在最后一个参数,例如:
>>> data = 'zABCgg123'
>>> a = re.search(r'abc',data,re.I)
>>> print(a)
<re.Match object; span=(1, 4), match='ABC'>
转义匹配
所说的转义匹配其实就是匹配类似\,*,.等此类字符,要匹配此类字符,只需在前面加上\(反斜杠)
>>> data = 'ni*hao'
>>> a = re.search(r'ni*hao', data) # 未进行转义,匹配会失败
>>> b = re.search(r'ni\*hao', data) # 使用反斜杠进行转义,可以匹配到
>>> print(a)
None
>>> print(b)
<re.Match object; span=(0, 6), match='ni*hao'>
哇,昨天写了一个晚上,写一遍自己的思路也清晰许多,所以说,好记性不如烂笔头!!