正则表达式

正则表达式是什么

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。正则表达式通常被用来检索、替换那些匹配某个模式的文本。

来点通俗易懂的,比如如下一个字符串:

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'>

哇,昨天写了一个晚上,写一遍自己的思路也清晰许多,所以说,好记性不如烂笔头!!