字符串是一种很重要的数据类型。很多程序都需要在一大串字符中查找满足特定规则的子串。(如匹配电话号码、邮箱等)
简而言之,正则表达式是记录文本规则的字符串代码。
我们当然可以自己通过复杂的算法写出一个个字符串匹配函数,但其实在实际开发过程中并没有这个必要。因为前人已经为我们做了这个工作。他们发明了正则表达式(regular expression)。
正则表达式描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
很多编程语言都对正则表达式进行了实现。Python也不例外。有关正则表达式的函数在都在re模块中。
import re
str_to_operate="要进行匹配的长字符串"
#通过模式字符串得到一个pattern对象regex
regex=re.compile(r"模式字符串") #r代表纯字符串,即不支持转义字符的字符串
#用regex去str_to_operate中匹配子串
#如果匹配到,返回一个match对象mo(match_object简称)
#如果没有匹配到,返回None
mo=regex.search(str_to_operate)
#通过mo的group()方法
#获取匹配到的子串
result=mo.group()
我们当然可以用某个字符(字母、数字)来匹配它本身。
如:
str_to_operate="1234567890abcd"
regex=re.compile(r"90ab")
mo=regex.search(str_to_operate)
print(mo.group())
#90ab
我们可以通过字符类型来匹配单个字符
字符分类/代码 | 含义 | 助记 |
---|---|---|
\d | 0-9中任意一个数字 | decimal |
\D | 除0-9的数字外的任意一个字符 | |
\w | 任意一个字母、数字,或下划线 | word |
\W | 除字母、数字,或下划线以外的任意一个字符 | |
\s | 一个空格、tab、或换行符 | space |
\S | 除空格、tab、或换行符外的任意一个字符 | |
. | 通配符,默认可以匹配除换行符外的任意字符 |
如:
str_to_operate="姓名:张三\n联系方式:15070997464\n家庭住址:警局"
regex=re.compile(r"\w\w\w\w:\d\d\d\d\d\d\d\d\d\d\d")
mo=regex.search(str_to_operate)
print(mo.group())
#联系方式:15070997464
()
分组匹配我们需要匹配的子串,通常由几部分含有各种含义的字符组成。如,需要匹配出生日期,它就由年、月、天组成。
我们可以在模式字符串中用()
标识分组。再用mo对象的group()通过索引获取对应组中的子串。
如:
import re
str_to_operate="姓名:张三"
regex=re.compile(r"(姓名):(张三)")
mo=regex.search(str_to_operate)
print(mo.group(0))
#姓名:张三
print(mo.group(1))
#姓名
print(mo.group(2))
#张三
print(mo.groups())
#('姓名', '张三')
r"字符串"会让字符串不支持转义字符,这里的转义字符是普通意义上的,如\n
换行
但是,在正则表达式中,也有很多特殊字符,比如这里的()
.
事实上,尽管有r,但compile()函数会支持正则表达式中的转义字符
如
. ^ $ * + ? { } [ ] \ | ( )
\. \^ \$ \* \+ \? \{ \} \[ \] \\ \| \( \)
|
多模式匹配我们需要匹配的字符串模式,通常不是一成不变的。同种含义的字符,可能有多种模式。如匹配张三以及他的电话号码,张三可能还有小名:小三,张小三等。
我们可以在模式字符串中用管道符|
标识多种匹配模式。只要满足其一,就可被匹配。
如:
import re
str_to_operate1="aaaaa张三:11011011011bbbbb"
str_to_operate2="aaaaa小三:12012012011bbbbb"
str_to_operate3="aaaaa张小三:13013013011bbbbb"
regex=re.compile(r"(张小三|小三|张小三):(\d\d\d\d\d\d\d\d\d\d\d)")
mo1=regex.search(str_to_operate1)
print(mo1.groups())
#('张三', '11011011011')
mo2=regex.search(str_to_operate2)
print(mo2.groups())
#('小三', '12012012011')
mo3=regex.search(str_to_operate3)
print(mo3.groups())
#('张小三', '13013013011')
?
可选匹配我们需要匹配的字符串模式,可能有些字符是允许有,也允许没有的。如匹配身份信息,配偶一项就可能没有。
我们可以使用?
来让它前面的字符或者分组变成可选的。即,有没有都可以匹配。
如:
str_to_operate1 = "aaaaa姓名:张三 配偶:李四 年龄:30bbbbb"
str_to_operate2 = "aaaaa姓名:王五 年龄:40bbbbb"
regex = re.compile(r"姓名:\w\w\w?\s(配偶:\w\w\w?\s)?年龄:\d\d\d?")
mo1 = regex.search(str_to_operate1)
print(mo1.group())
#姓名:张三 配偶:李四 年龄:30
mo2 = regex.search(str_to_operate2)
print(mo2.group())
#姓名:王五 年龄:40
即*
前面的字符或分组出现零次或者多次,都能匹配上。
str_to_operate1="hellooooooooooooooooo!"
str_to_operate2="hello!"
str_to_operate3="hell!"
regex=re.compile(r"hello*")
mo1=regex.search(str_to_operate1)
print(mo1.group())
#hellooooooooooooooooo
mo2=regex.search(str_to_operate2)
print(mo2.group())
#hello
mo3=regex.search(str_to_operate3)
print(mo3.group())
#hell
+
匹配一次或多次str_to_operate1="hellooooooooooooooooo!"
str_to_operate2="hello!"
str_to_operate3="hell!"
regex=re.compile(r"hello+")
mo1=regex.search(str_to_operate1)
print(mo1.group())
#hellooooooooooooooooo
mo2=regex.search(str_to_operate2)
print(mo2.group())
#hello
mo3=regex.search(str_to_operate3)
print(mo3.group())
#None
{}
匹配特定次数{m}
,只有前面的字符出现m次,才能匹配上
{m,n}
,前面的字符出现m到n次(包含m,n次),才能匹配上
{m,n}默认匹配能够匹配的最长子串。这称为贪心匹配。
在{m,n}后加上?,即{m,n}?,可以变成非贪心匹配。即匹配能够匹配的最短子串。
如:
str_to_operate1="hellooooo!"
str_to_operate2="hellooo!"
str_to_operate3="hello!"
#3次
regex1=re.compile(r"hello{3}")
mo1=regex1.search(str_to_operate1)
print(mo1.group())
#hellooo
mo2=regex1.search(str_to_operate2)
print(mo2.group())
#hellooo
mo3=regex1.search(str_to_operate3)
print(mo3)
#None
#贪心2到5次
regex2=re.compile(r"hello{2,5}")
mo4=regex2.search(str_to_operate1)
print(mo4.group())
#hellooooo
mo5=regex2.search(str_to_operate2)
print(mo5.group())
#hellooo
mo6=regex2.search(str_to_operate3)
print(mo6)
#None
#非贪心2到5次
regex3=re.compile(r"hello{2,5}?")
mo7=regex3.search(str_to_operate1)
print(mo7.group())
#helloo
mo8=regex3.search(str_to_operate2)
print(mo8.group())
#helloo
search只会匹配第一个能被匹配的子串,而findall能匹配所有能够被匹配的子串。并且直接返回由 子串的组组成的元组 组成的列表
str_to_operate="name='张三' age=18; name='李四' age=19;name='王五' age=20;"
regex=re.compile(r"name='(\w\w)' age=(\d\d);")
mo=regex.search(str_to_operate)
print(mo.groups())
#('张三', '18')
result=regex.findall(str_to_operate)
print(result)
#[('张三', '18'), ('李四', '19'), ('王五', '20')]
[]
中的字符都能匹配。
如[abc123]能够匹配abc123中的任意一个字符
如果是连续的字母或者连续的数字,还可以用[a--z]
、[A-Z]
、``[0-9]`这种简写形式
如:
str_to_operate="第一名:张三;第2名:李四;第三名:王五;第4名:赵六;"
regex=re.compile(r"第([1-3一二三])名:(\w\w)")
result=regex.findall(str_to_operate)
print(result)
#[('一', '张三'), ('2', '李四'), ('三', '王五')]
在[]
中开头加一个^
,就代表只要有[]
中的字符就不能匹配,除此之外的其他字符都能匹配。
pattern对象的sub方法可以用来替换匹配到的子串。
str_to_operate="......"
regex=re.compile(r"模式字符串")
result=regex.sub(new_str,str_to_operate)
在new_str中,还可以加上\group_num
来使用匹配到的指定分组中的字符串
如:
str_to_operate="name:Zhang San"
regex=re.compile(r"(name):(.+)")
result=regex.sub(r"NAME:Mr.\2.",str_to_operate)
print(result)
#NAME:Mr.Zhang San.
^
和$
定位匹配^
表示,只有以regex模式开头,才能被匹配。放在regex模式开头。
$
表示,只有以regex模式结束,才能被匹配。放在regex模式末尾。
str_to_operate="hello,Jane.hello,Jack.Rose,hello"
regex1=re.compile(r"^hello,(\w+).")
regex2=re.compile(r"(\w+),hello$")
mo1=regex1.search(str_to_operate)
mo2=regex2.search(str_to_operate)
print(mo1.groups())
#('Jane',)
print(mo2.groups())
#('Rose',)
在compile生成pattern对象时,可以传入一些flag参数
regex=re.compile("匹配模式",flag1|flag2|...)
flag值 | 含义 |
---|---|
re.VERBOSE | 忽略空白符和注释 |
re.IGNORECASE | 忽略大小写 |
re.DONALL | 让通配符可以匹配换行符 |