1、第8章 正则表达式董付国微信公众号:Python小屋1第8章 正则表达式 正则表达式是字符串处理的有力工具,正则表达式使用预定义的模式去匹配一类具有共同特征的字符串,可以快速、准确地完成复杂的查找、替换等处理要求,比字符串自身提供的方法提供了更强大的处理功能。例如使用字符串对象的split()方法只能指定一个分隔符,而使用正则表达式可以很方便地指定多个符号作为分隔符;使用字符串对象的split()并指定分隔符时,很难处理分隔符连续多次出现的情况,而正则表达式让这一切都变成非常轻松。正则表达式在文本编辑与处理、网页爬虫之类的场合中有重要应用。28.1 正则表达式语法 正则表达式由元字符及其不同组
2、合来构成,通过巧妙地构造正则表达式可以匹配任意字符串,并完成查找、替换、分隔等复杂的字符串处理任务。38.1.1 正则表达式基本语法4元字符元字符功能说明功能说明.匹配除换行符以外的任意单个字符*匹配位于*之前的字符或子模式的0次或多次出现+匹配位于+之前的字符或子模式的1次或多次出现-在之内用来表示范围|匹配位于|之前或之后的字符匹配行首,匹配以后面的字符开头的字符串$匹配行尾,匹配以$之前的字符结束的字符串?匹配位于?之前的0个或1个字符。当此字符紧随任何其他限定符(*、+、?、n、n,、n,m)之后时,匹配模式是“非贪心的”。“非贪心的”模式匹配搜索到的、尽可能短的字符串,而默认的“贪心
3、的”模式匹配搜索到的、尽可能长的字符串。例如,在字符串“oooo”中,“o+?”只匹配单个“o”,而“o+”匹配所有“o”表示位于之后的为转义字符num此处的num是一个正整数,表示子模式编号。例如,“(.)1”匹配两个连续的相同字符f换页符匹配n换行符匹配8.1.1 正则表达式基本语法5元字符元字符功能说明功能说明r匹配一个回车符b匹配单词头或单词尾B与b含义相反d匹配任何数字,相当于0-9D与d含义相反,等效于0-9s匹配任何空白字符,包括空格、制表符、换页符,与 fnrtv 等效S与s含义相反w匹配任何字母、数字以及下划线,相当于a-zA-Z0-9_W与w含义相反w含义相反,与“A-Za
4、-z0-9_”等效()将位于()内的内容作为一个整体来对待m,n前的字符或子模式重复至少m次,至多n次表示范围,匹配位于中的任意一个字符xyz反向字符集,匹配除x、y、z之外的任何字符a-z字符范围,匹配指定范围内的任何字符a-z反向范围字符,匹配除小写英文字母之外的任何字符8.1.1 正则表达式基本语法 如果以“”开头的元字符与转义字符相同,则需要使用“”,或者使用原始字符串。在字符串前加上字符r或R之后表示原始字符串,字符串中任意字符都不再进行转义。原始字符串可以减少用户的输入,主要用于正则表达式和文件路径字符串的情况,但如果字符串以一个斜线“”结束的话,则需要多写一个斜线,即以“”结束。
5、68.1.2 正则表达式扩展语法 正则表达式使用圆括号“()”表示一个子模式,圆括号内的内容作为一个整体对待,例如(red)+可以匹配redred、redredred等一个或多个重复red的情况。使用子模式扩展语法可以实现更加复杂的字符串处理功能。78.1.2 正则表达式扩展语法8语法功能说明(?P)为子模式命名(?iLmsux)设置匹配标志,可以是几个字母的组合,每个字母含义与编译标志相同(?:.)匹配但不捕获该匹配的子表达式(?P=groupname)表示在此之前的命名为groupname的子模式(?#.)表示注释(?=)用于正则表达式之前,表示如果=后的内容在字符串中不出现则匹配,但不返
6、回=之后的内容(?=)用于正则表达式之后,表示如果=后的内容在字符串中出现则匹配,但不返回=之后的内容(?!.)用于正则表达式之前,表示如果!后的内容在字符串中不出现则匹配,但不返回!之后的内容(?!.)用于正则表达式之后,表示如果!后的内容在字符串中不出现则匹配,但不返回!之后的内容8.1.3 正则表达式集锦最简单的正则表达式是普通字符串,可以匹配自身pjcython可以匹配python、jython、cythona-zA-Z0-9可以匹配一个任意大小写字母或数字abc可以一个匹配任意除a、b、c之外的字符python|perl或p(ython|erl)都可以匹配python或perl子模式
7、后面加上问号表示可选。r(http:/)?(www.)?python.org只能匹配http:/www.python.org、http:/python.org、www.python.org和python.orghttp只能匹配所有以http开头的字符串(pattern)*:允许模式重复0次或多次(pattern)+:允许模式重复1次或多次(pattern)m,n:允许模式重复mn次98.1.3 正则表达式集锦(a|b)*c:匹配多个(包含0个)a或b,后面紧跟一个字母c。ab1,:等价于ab+,匹配以字母a开头后面带1个至多个字母b的字符串。a-zA-Z1(a-zA-Z0-9._)4,19$:
8、匹配长度为5-20的字符串,必须以字母开头并且可带字母、数字、“_”、“.”的字符串。(w)6,20$:匹配长度为6-20的字符串,可以包含字母、数字、下划线。d1,3.d1,3.d1,3.d1,3$:检查给定字符串是否为合法IP地址。(134-9d8)|(1501289d8)$:检查给定字符串是否为移动手机号码。a-zA-Z+$:检查给定字符串是否只包含英文字母大小写。w+(w+.)+w+$:检查给定字符串是否为合法电子邮件地址。r(w)(?!.*1):查找字符串中每个字符的最后一次出现。r(w)(?=.*1):查找字符串中所有重复出现的字符。108.1.3 正则表达式集锦(-)?d+(.d
9、1,2)?$:检查给定字符串是否为最多带有2位小数的正数或负数。u4e00-u9fa5:匹配给定字符串中所有汉字。d18|d15$:检查给定字符串是否为合法身份证格式。d4-d1,2-d1,2:匹配指定格式的日期,例如2016-1-31。(?=.*a-z)(?=.*A-Z)(?=.*d)(?=.*,._).8,$:检查给定字符串是否为强密码,必须同时包含英语字母大写字母、英文小写字母、数字或特殊符号(如英文逗号、英文句号、下划线),并且长度必须至少8位。(?!.*/;=%?).+:如果给定字符串中包含、”、/、;、=、%、?则匹配失败。(.)1+:匹配任意字符的一次或多次重复出现。(?Pbw+
10、b)s+(?P=f):匹配连续出现两次的单词。(?P.)(?P=f)(?P.)(?P=g):匹配AABB形式的成语或字母组合。118.1.3 正则表达式集锦 使用时要注意的是,正则表达式只是进行形式上的检查,并不保证内容一定正确。例如上面的例子中,正则表达式d1,3.d1,3.d1,3.d1,3$可以检查字符串是否为IP地址,字符串888.888.888.888这样的也能通过检查,但实际上并不是有效的IP地址。同样的道理,正则表达式d18|d15$也只负责检查字符串是否为18位或15位数字,并不保证一定是合法的身份证号。128.2 直接使用正则表达式模块re处理字符串 Python标准库re模
11、块提供了正则表达式操作所需要的功能。13方法方法功能说明功能说明findall(pattern,string,flags)返回包含字符串中所有与给定模式匹配的项的列表match(pattern,string,flags)从字符串的开始处匹配模式,返回match对象或Nonesearch(pattern,string,flags)在整个字符串中寻找模式,返回match对象或Nonesplit(pattern,string,maxsplit=0)根据模式匹配项分隔字符串sub(pat,repl,string,count=0)将字符串中所有与pat匹配的项用repl替换,返回新字符串,repl可以是
12、字符串或返回字符串的可调用对象,作用于每个匹配的match对象8.2 直接使用正则表达式模块re处理字符串 其中函数参数“flags”的值可以是下面几个的不同组合(使用“|”进行组合):re.I(注意是大写字母I,不是数字1,表示忽略大小写)re.L(支持本地字符集的字符)re.M(多行匹配模式)re.S(使元字符“.”匹配任意字符,包括换行符)re.U(匹配Unicode字符)re.X(忽略模式中的空格,并可以使用#注释)148.2 直接使用正则表达式模块re处理字符串15 import re#导入re模块 text=alpha.beta.gamma delta#测试用的字符串 re.spl
13、it(.+,text)#使用指定字符作为分隔符进行分隔alpha,beta,gamma,delta re.split(.+,text,maxsplit=2)#最多分隔2次alpha,beta,gamma delta re.split(.+,text,maxsplit=1)#最多分隔1次alpha,beta.gamma delta pat=a-zA-Z+re.findall(pat,text)#查找所有单词alpha,beta,gamma,delta8.2 直接使用正则表达式模块re处理字符串16 pat=name text=Dear name.re.sub(pat,Mr.Dong,text)#
14、字符串替换Dear Mr.Dong.s=a s d re.sub(a|s|d,good,s)#字符串替换good good good s=Its a very good good idea re.sub(r(bw+)1,r1,s)#处理连续的重复单词Its a very good idea re.sub(r(w+)1,r2,s)Its a very goodidea re.sub(a,lambda x:x.group(0).upper(),aaa abc abde)#repl为可调用对象AAA Abc Abde8.2 直接使用正则表达式模块re处理字符串17 re.sub(a-z,lambda
15、 x:x.group(0).upper(),aaa abc abde)AAA ABC ABDE re.sub(a-zA-z,lambda x:chr(ord(x.group(0)32),aaa aBc abde)#英文字母大小写互换AAA AbC ABDE re.subn(a,dfg,aaa abc abde)#返回新字符串和替换次数(dfgdfgdfg dfgbc dfgbde,5)re.sub(a,dfg,aaa abc abde)dfgdfgdfg dfgbc dfgbde re.escape(http:/www.python.org)#字符串转义http:/www.python.org
16、8.2 直接使用正则表达式模块re处理字符串 print(re.match(done|quit,done)#匹配成功,返回match对象 print(re.match(done|quit,done!)#匹配成功 print(re.match(done|quit,doe!)#匹配不成功,返回空值NoneNone print(re.match(done|quit,d!one!)#匹配不成功None print(re.search(done|quit,d!one!done)#匹配成功188.2 直接使用正则表达式模块re处理字符串 下面的代码使用不同的方法删除字符串中多余的空格,如果遇到连续多个空格
17、则只保留一个,同时删除字符串两侧的所有空白字符。import re s=aaa bb c d e fff .join(s.split()#直接使用字符串对象的方法aaa bb c d e fff .join(re.split(s+,s.strip()#同时使用re中的函数和字符串对象的方法aaa bb c d e fff .join(re.split(s+,s.strip()#与上一行代码等价aaa bb c d e fff re.sub(s+,s.strip()#直接使用re模块的字符串替换方法aaa bb c d e fff198.2 直接使用正则表达式模块re处理字符串 下面的代码使用几
18、种不同的方法来删除字符串中指定内容:email=tonytiremove_ m=re.search(remove_this,email)#使用search()方法返回的match对象 email:m.start()+emailm.end():#字符串切片 re.sub(remove_this,email)#直接使用re模块的sub()方法 email.replace(remove_this,)#直接使用字符串替换方法208.2 直接使用正则表达式模块re处理字符串 下面的代码使用以“”开头的元字符来实现字符串的特定搜索。import re example=Beautiful is better
19、 than ugly.re.findall(bb.+?b,example)#以字母b开头的完整单词#此处问号?表示非贪心模式better re.findall(bb.+b,example)#贪心模式的匹配结果better than ugly re.findall(bbw*b,example)better re.findall(Bh.+?b,example)#不以h开头且含有h字母的单词剩余部分han218.2 直接使用正则表达式模块re处理字符串 re.findall(bw.+?b,example)#所有单词Beautiful,is,better,than,ugly re.findall(w+
20、,example)#所有单词Beautiful,is,better,than,ugly re.findall(rbw.+?b,example)#使用原始字符串Beautiful,is,better,than,ugly re.split(s,example)#使用任何空白字符分隔字符串Beautiful,is,better,than,ugly.re.findall(d+.d+.d+,Python 2.7.13)#查找并返回x.x.x形式的数字2.7.13 re.findall(d+.d+.d+,Python 2.7.13,Python 3.6.0)2.7.13,3.6.0 s=This is h
21、ead.This is body.pattern=r(.+)(.+)result=re.search(pattern,s)result.group(1)#第一个子模式This is head.result.group(2)#第二个子模式This is body.228.3 match对象23n正则表达式对象的match方法和search方法匹配成功后返回match对象。match对象的主要方法有:group():返回匹配的一个或多个子模式内容groups():返回一个包含匹配的所有子模式内容的元组groupdict():返回包含匹配的所有命名子模式内容的字典start():返回指定子模式内容的
22、起始位置end():返回指定子模式内容的结束位置的前一个位置span():返回一个包含指定子模式内容起始位置和结束位置前一个位置的元组。8.3 match对象 m=re.match(r(w+)(w+),Isaac Newton,physicist)m.group(0)#返回整个模式内容Isaac Newton m.group(1)#返回第1个子模式内容Isaac m.group(2)#返回第2个子模式内容.Newton m.group(1,2)#返回指定的多个子模式内容(Isaac,Newton)248.3 match对象25 m=re.match(r(?Pw+)(?Pw+),Malcolm
23、Reynolds)m.group(first_name)#使用命名的子模式Malcolm m.group(last_name)Reynolds m=re.match(r(d+).(d+),24.1632)m.groups()#返回所有匹配的子模式(不包括第0个)(24,1632)m=re.match(r(?Pw+)(?Pw+),Malcolm Reynolds)m.groupdict()#以字典形式返回匹配的结果first_name:Malcolm,last_name:Reynolds8.3 match对象 s=aabc abcd abbcd abccd abcdd re.findall(r(
24、bw*(?Pw+)(?P=f)w*b),s)(aabc,a),(abbcd,b),(abccd,c),(abcdd,d)268.4 精彩案例赏析 例例8-1 使用正则表达式提取字符串中的电话号码。import retext=Suppose my Phone No.is 0535-1234567,yours is 010-12345678,his is 025-87654321.#注意,下面的正则表达式中大括号内逗号后面不能有空格matchResult=re.findall(r(d3,4)-(d7,8),text)for item in matchResult:print(item0,item1
25、,sep=-)278.4 精彩案例赏析 例例8-2 使用正则表达式查找文本中最长的数字字符串。import redef longest1(s):查找所有连续数字 t=re.findall(d+,s)if t:return max(t,key=len)return Nodef longest2(s):使用非数字作为分隔符 t=re.split(d+,s)if t:return max(t,key=len)return No288.4 精彩案例赏析 例例8-3 将一句英语文本中的单词进行倒置,标点不倒置,假设单词之间使用一个或多个空格进行分割。比如I like beijing.经过函数后变为:beijing.like I。import redef reverse(s):t=re.split(s+,s.strip()t.reverse()return .join(t)print(reverse(I like beijing.)print(reverse(Simple is better than complex.)29