python学习-正则表示式及re模块

时间:2021-02-28 22:36:58

python中的所有正则表达式函数都在re模块中。import re导入该模块。

1,创建正则表达式对象

想re.compile()传入一个字符串值,表示正则表达式,它将返回一个Regex模式对象。

创建一个匹配手机号的(\d表示一个数字字符)

mphone_regex = re.compile(r'\d\d\d-\d\d\d\d-\d\d\d\')

2,匹配正则表达式

regex对象的search()方法查找传入的字符串,寻找该正则表达式的所有匹配。如果字符串中没有找到该正则表达式模式,search()方法返回None。如果找到该模式,search()方法返回一个Match对象,

Match对象有一个group()方法,它返回被查找字符串中实际匹配的文本,如下

import re
mphone_regex = re.compile(r'\d\d\d-\d\d\d\d-\d\d\d\d')
mo = mphone_regex.search('My mobile Phone Number is 133-1111-1111.')
print('number is:%s'%mo.group())

输出:
number is:133-1111-1111

注意:字符串中的\n,\t分表表示换行、制表符,\是个转义字符,\\才表示一个\。如果在字符串的前面加上r,可以将该字符串标记为原始字符串,它不包括转移字符。

其顺序为:

  • import re 导入re模块
  • 用re.compile()创建一个Regex对象,最好带上r
  • 想Regex对象的search()方法传入想查找的字符串。它返回一个Match对象
  • 调用Match对象的group()方法,返回实际匹配文本的字符串

上面的也可以这样写:

import re
mo = re.search(r'\d{3}-\d{4}-\d{4}','My mobile Phone Number is 133-1111-1111')
print('number is:%s'%mo.group())

3,用括号分组

如果我只想要手机号的前三位怎么办?

import re
mo = re.search(r'(\d{3})-(\d{4}-\d{4})',r'My mobile Phone Number is 133-1111-1111') #用()将正则表达式分成两组

print('number is:%s'%mo.group())     #group()没有参数或者参数0表示整个匹配的文本
print('number is:%s'%mo.group(0))
print('number is:%s'%mo.group(1))   #1表示第一对括号匹配的内容
print('number is:%s'%mo.group(2))   #1表示第一对括号匹配的

输出:

number is:133-1111-1111
number is:133-1111-1111
number is:133
number is:1111-1111

如果要一次获取所有的分组,就使用groups()。

import re
mo = re.search(r'(\d{3})-(\d{4}-\d{4})',r'My mobile Phone Number is 133-1111-1111') #用()将正则表达式分成两组
print(mo.groups())  #groups返回包含多个值的元组
first,last = mo.groups()
print(first)
print(last)
输出:
('133', '1111-1111')
133
1111-1111

那么问题来了,既然()在正则表达式中有特殊的含义,那么如何匹配文本中的()呢?使用\进行转移,\(内容\)

import re
mo = re.search(r'(\(\d{3}\))-(\d{4}-\d{4})',r'My mobile Phone Number is (133)-1111-1111') #用()将正则表达式分成两组
print(mo.group())
print(mo.group(1))

输出:
(133)-1111-1111
(133)

4,使用管道匹配多个分组

比如laowang和xiaozhang都出现在被查找的字符串中,第一次出现的匹配文本,将作为Match对象返回。

import re
name = re.search(r'laowang|xiaozhang','We team include laowang and xiaozhang.')
print(name.group()) #第一个先匹配到谁,谁将作为Match对象返回

输出:
laowang

使用管道来匹配多个模式中的一个

import re
name = re.search(r'lao(wang|zhang|liu|luo|cc)','We team include laoliu,laoluo,laowang and laozhang.')
print(name.group())
print(name.group(1))
输出:
laoliu
liu

name.group()返回完全匹配的文本‘laoliu’,name.grop(1)只返回第一个括号分组中匹配到的文本‘liu’。可以使用管道和()组合去匹配。另外如果匹配真正的|,需要转意\|

import re
name = re.search(r'lao(wang|zhang|liu|luo|cc)-(er|san|si|wu)','We team include laoliu-qq,laoluo-er,laowang-sfs and laozhang.')
print(name.group())
print(name.group(1))
print(name.group(2))

输出:
laoluo-er
luo
er

5,用问号实现可选匹配

有时候,相匹配的模式是可选的。就是说,无论这段文本存不存在,正则表达式都会存在。

>>> phone = re.search(r'(\d{3}-)?\d{4}-\d{4}','phone is 1234-1234')
>>> phone.group()
'1234-1234'
>>> phone = re.search(r'(\d{3}-)?\d{4}-\d{4}','phone is 021-1234-1234')
>>> phone.group()
'021-1234-1234'
>>> 

(\d{3}-)?,()里的内容可选,有则匹配,没有则不匹配。也可以这样想匹配这个问号前的分组0次或者1次。

如果需要匹配真正的?,需要\?

6,用星号匹配0次或多次

和?差不多,只不过*前的分组既可以匹配0次,也可以匹配任意次。

>>> name = re.search(r'(too)*good','you are tootootoogood!')
>>> name.group()
'tootootoogood'
>>> name = re.search(r'(too)*good','you are good!')
>>> name.group()
'good'
>>> 

7,用加号匹配一次或多次

跟*使用一样,只不过*前的分组可以匹配0次。但是+前的分组至少匹配一次

8,用花括号匹配特定次数

如果要一个分组重复特定次数,就在正则表达式该分组的后面,写上花括号,并在{}里面写上需要重复的次数

如(too){3}表示将匹配3次too,(\d){1,4},表示匹配数字1个,2个,3个或4个数字。而(\d){3,}表示匹配3个或更多的数字,(\d){,5}表示匹配0到5个数字。

>>> name.group()
'tootoogood'
>>> name = re.search(r'(too){3}good','you are tootootoogood!')
>>> name.group()
'tootootoogood'
>>> num = re.search(r'(\d){,4}-a','afafa afaf a12334 43af -a')
>>> num.group()
'-a'
>>> 

9,贪婪匹配与非贪婪匹配

如下面:,匹配1-4个too,还可以返回toogood,tootoogood,为什么会是tootootoogood呢?

>>> name = re.search(r'(too){1,4}good','you are tootootoogood!')
>>> name.group()
'tootootoogood'

Python正则表达式默认是“贪婪”的,这表示在多义的情况下,它们会尽可能匹配最长的字符串。花括号的"非贪婪"匹配尽可能最短的字符串,在花括号后面加个?,即{}?

>>> name = re.search(r'd(o){1,5}','you are dooooo!')
>>> name.group()
'dooooo'
>>> name = re.search(r'd(o){1,5}?','you are dooooo!')
>>> name.group()
'do'
>>> 

这个还未完全搞明白,待补充。。。。。。

10,findall()方法

search()只会返回第一个匹配到的文本,且返回的是一个Match对象,需要使用group()或者groups()查看

findall()不是返回一个Match对象,而是返回一个包含所有匹配项的列表

  • 如果调用在一个没有分组的正则表达式上,findall()将返回一个匹配字符串的列表
  • 如果调用在一个有分组的正则表达式上,findall()将返回一个包含字符串的元组构成的列表(每个分组对应元组中的一个字符串)(分组外的-不会返回,如下面例子)
>>> phone = re.findall(r'\d{3}-\d{3}-\d{3}','number is 123-456-789and111-111-111 and 123-456-789')
>>> phone
['123-456-789', '111-111-111', '123-456-789']
>>> phone = re.findall(r'(\d{3})-(\d{3}-\d{3})','number is 123-456-789and111-111-111 and 123-456-789')
>>> phone
[('123', '456-789'), ('111', '111-111'), ('123', '456-789')]

11,不区分大小写的匹配

使用re.I 或者re.IGNORECASE作为正在表达式的第二个参数

>>> name = re.search(r'jack,mike','there are some boys:Jack,Mike',re.I)
>>> name.group()
'Jack,Mike'
>>> 

 12,插入字符和美元字符

可以在正则表达式的开始处使用插入符号(^),表明匹配必须发生在被查找文本开始处。类似的,可以在正则表达式的末尾加上美元符号($),表示该字符串必须以这个正则表达式的模式结束。可以同时使用^和$,表明整个字符串必须匹配该模式。

>>> name
[]
>>> name = re.findall(r'^hel','Hello World !',re.I)
>>> name
['Hel']
>>> 
>>> name = re.findall(r'world !$','Hello World !',re.I)
>>> name
['World !']

13,通配字符

在正则表达式中,.(句点)字符称为“通配符”。它匹配除了换行符之外的所有字符。

>>> at = re.findall(r'.at','hat,cat,sat,mat')
>>> at
['hat', 'cat', 'sat', 'mat']
>>> 

句点字符只匹配一个字符。要匹配真正的句点,要转意,\. 。

14,用点星匹配所有字符

句点表示除换行外的所有单个字符,星号字符表示“前面字符出现0次或多次”。

>>> name = re.search(r'ab.*gh','123abcdefghijk')
>>> name
>>> name.group()
'abcdefgh'
>>> 

点星号使用贪婪模式和非贪婪模式:默认是贪婪匹配,使用非贪婪匹配,使用.*?   

>>> book = re.search('<.*>','<<Python>>,<<shell>>')
>>> book.group()
'<<Python>>,<<shell>>'
>>> book = re.search('<.*?>','<<Python>>,<<shell>>')
>>> book.group()
'<<Python>'
>>> 

15,用句点字符匹配换行符

 点星将匹配除换行外的所有字符。通过传入re.DATALL参数,可以让句点字符匹配所有字符,也包括换行。

import re
txt = """
every night in my dreams
i see you, i feel you
that is how i know you go on\nfar across the distance
"""
test1 = re.search(r'in my.*far',txt,re.DOTALL)
test2 = re.search(r'in my.*far',txt,)
print(test1.group())

输出:
in my dreams
i see you, i feel you
that is how i know you go on
far

16,建立自己的字符分类

有时候你想匹配一组字符,但是\d,\w,\s匹配的太宽泛了,你可以用方括号定义紫的字符分类。例如字符分类[aeiouAEIOU]将匹配所有原音字符。

>>> re.findall(r'[aeiouAEIOU]','I am lao cui')
['I', 'a', 'a', 'o', 'u', 'i']
>>> 

也可以使用短横线表示字母或数字的范围。例如,字符分类[a-zA-Z0-9]将匹配所有大小写字母和数字。

请注意,在方括号内,普通的正则表达式符号不会被解释,这意味着,你不需要前面加上\转义,如果你需要匹配数字和一个*,直接[0-9*]即可,不需要[0-9\*]。

通过在方括号内第一个位置加上一个^,就可以得到‘非字符类’。非字符类将匹配不在这个字符类中的所有字符。如下:

>>> re.findall(r'[^aeiouAEIOU]','I am lao cui')
[' ', 'm', ' ', 'l', ' ', 'c']
>>> 

17,用sub()方法替换字符串

正则表达式不仅能找到文本,还能够用新的文本替换掉这些模式,Regex的sub()方法需要传入两个参数,第一个参数是一个字符串,用来替换匹配到的字符串。

import re
num = re.compile(r'\d{3}')
test = num.sub('***','123-456-abc')
print(test)

输出:
***-***-abc

也可以这样:

test = re.sub(r'\d{3}','***','123-456-abc')
print(test)

18,使用多个参数

默认只能接受一个参数,可以使用管道符|

txt = """
Every night In my dreams
I see you, i feel you
that is how i know you go on\nfar across the distance
"""
test1 = re.search(r'in my.*far',txt,re.DOTALL|re.I)
print(test1.group())
输出:
In my dreams
I see you, i feel you
that is how i know you go on
far

既不区分大小写,又能匹配换。

19,re.VERBOSE参数,常用来管理复杂的正则表达式

如果要匹配一个复杂的文本,会需要一个既长又费解的正则表达式,可以使用re.VERBOSE参数来忽略正则表达式中字符串中的空白符和注释。

import re
phoneregex = re.compile(r"""(
    (\d{3}|\(\d{3}\))?      #匹配区号可选,123或者(123),注意管道符,和后面括号的转义
    (\s|-|\.)?          #可选连接符,空格或者-或者
    (\d{4})         #前4位号码
    (\s|-|\.)?          #可选连接符,空格或者-或者
    (\d{4})         #后4为号码
    (\s*(ext|x|etx\.)\s*(\d{2,5}))?  #可选的分机号,包括任意的空格接着ext或x或etx.再接任意空格,2到5个数字
)""",re.VERBOSE
)

num = phoneregex.findall('38637788,3863-1234- x23,010-1234-2345 etx.145')
print(num)
输出:

[('38637788', '', '', '3863', '', '7788', '', '', ''), ('3863-1234', '', '', '3863', '-', '1234', '', '', ''), ('010-1234-2345 etx.145', '010', '-', '1234', '-', '2345', ' etx.145', 'etx.', '145')]

注意:如果外面不加括号,那么列表中每个元组的第一个就不是完整的电话号了,就是各个分组。参考上面的()分组,管道和findall()。