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()。