CDA数据分析师出品在前两篇连载文章中,我们学习了re模块的match()、search()、findall()方法,并学习了如何使用正则表达式将表达式中常用的元字符、限定符、选择字符、方括号与这些方法结合使用,可以灵活处理常见的数据匹配问题。在本篇文章中,我们将进一步研究正则表达式中的其他匹配,包括令初学者头疼的分组问题。排除字符首先我们回顾一下上一篇连载中最后一个例子:pattern='[aA-zZ]+'message='企业名称:CDA数据科学研究所nEmail:1918560461@qq.comn地址:北京地址:1198,广州市黄埔区虎林路516室n网址:http://www.cda.cnn'findall=re.findall(pattern,message)print(findall)out:'CDA','qq','com','www','cda','cn','qq','com','www','cda','cn']通过模式字符串中的方括号,我们可以匹配所有英文字符串字符串,但是如果反过来,如何提取所有非英文字符串呢?这时可以使用排除符“^”,放在方括号中表示排除。只需将其放在模式字符串方括号内的第一个字符位置:pattern='1+'message='企业名称:CDA数据科学研究所n邮箱:1918560461@qq.comn地址:2楼,北京市海淀区长洼街3号2号楼网址:http://www.cda.cnn企业名称:广州学习在线科技有限公司邮箱:981856661@qq.com地址:北京市海淀区长洼街3号516室地址:广州市黄埔区虎林路1198号网址:http://www.cda.cnn'findall=re.findall(pattern,message)print(findall)out:['公司名称:','数据科学研究院n邮箱:1918560461@','.','n地址:北京市海淀区长洼街3号2号楼2层n网址:','.','.','n企业名称:广州学习在线科技有限公司nEmail:981856661@','.','n地址:广州市黄埔区虎林路1198号516室n网址:','.','.','n']这就是我们会发现的,通过使用排除字符,e提取了常见的分隔符,例如换行符和逗号。可以看出,排除字符“^”的本质是将要排除的字符作为分隔符,将要排除的字符一一提取出来。上面那个麻烦组解释了正则表达式方括号中排除字符的作用。与方括号相比,表达式中的圆括号是很多初学者的噩梦。:它无处不在,但它所扮演的角色却常常令人困惑。一方面是因为这方面没有太多通俗易懂的学习资料。一方面,括号符号可以显示正则表达式中两种不同的功能方式。首先,让我们看一下括号的第一种工作方式:分组。正则表达式中提到的分组与许多编程语言中的groupby操作有很大的不同。本着由浅入深的精神,我们举一个简单的例子如下:pattern='(six)th'message='sixthfourth'search=re.search(pattern,message)searchsearch.group(0)search.group(1)out:'sixth''six'我们发现使用模式字符串'(six)th'可以匹配字符串'sixthfourth''sixth'in'.如果我们在返回的re.match对象中调用.group(n)方法,我们会惊奇地发现search.group(0)返回的是第一个匹配到的对象sixth'。search.group(1)返回另一个匹配对象“六”!.也就是说,带括号的模式字符串匹配两次!·第一次是忽略括号,直接使用模式串'sixth'匹配'sixth'。·第二次是将模式串中括号内的字符串'six'作为第二次匹配的子模式串,得到匹配结果'six'。我们可以把括号内的字符串看作模式字符串的一个“子表达式”。我们再举一个例子。比如我们要在字符串'sixthseventh'中匹配'six','sixth','seven','seventh'四个字符串,使用re.findall()方法看效果:pattern='(six|seven)th'message='sixthseventh'findall=re.findall(pattern,message)findallout:['six','seven']我们会发现我们使用了括号,括号内的选择字符"|“也用了,括号右边有个“th”,但是第6和第7还是没有匹配上。这其实是很多同学初学正则表达式时经常踩到的一个“坑”,即括号在re.search()中的作用方式与在re.findall()中的作用方式不同。re.findall()在识别模式串时,一旦遇到括号,就会匹配括号内的模式串,忽略括号外的模式串。在上面的例子中,模式字符串中的“th”在括号外,这就是为什么前面例子中的re.findall()没有匹配到sixthth和seventh的原因。那么遇到这种情况我们应该怎么办呢?由于re.findall()方法只匹配括号内的模式串,所以我们可以在之前的模式串的最外层再嵌套一层括号,看看效果:pattern='((six|seven)th)'message='sixthseventh'findall=re.findall(pattern,message)findallout:[('sixth','six'),('seventh','seven')]我们会发现,'six','sixth','seven'和'seventh'都匹配,'sixth','six'被“打包”在一个元组中。运算符优先级的问题上面的例子其实可以体现出括号可以限制选择符“|”的范围。此外,括号还可以限制元字符的范围。这其实反映了在正则表达式中,括号的运算符优先级高于选择符。那么,正则表达式中所有运算符的优先级顺序是怎样的呢?我们可以看下表:那么,既然正则表达式的括号有更高的优先级,效果如何呢?我们可以将它与限定符一起使用,来匹配一些在一行中重复出现的字符串,并且每个字符串都有相同的内部规则,比如每次都是连续出现的日期。首先,我们举一个简单的例子。例如,我们要匹配特定格式的日期。找到日期字符串模式后,我们将模式字符串定义为以下形式:pattern=r'[0-9]{4}year[0-9]{1,2}Month[1-9]{1,2}day'message='今天是2020年3月1日,2019年7月17日发生的一件事至今记忆犹新'findall=re.findall(pattern,message)findallout:['2020年3月1日','July17,2019']可以看出,由于上面出现的日期,其内部是有规律的,因此我们将这些日期的内在规律写成一个模式字符串来匹配所有的日期。但是如果上面例子中消息中的某些日期不正常,每个日期都会出现两次,例如:message='TodayisMarch1,2020March1,2020March1,2019,July17,20197月发生的事件2019年17日,我还记忆犹新。这就是我之前说的,里面是有规律的,连续出现的次数也是有规律的。这时候我们就可以用括号把前面例子中的模式“包装”起来。括号中的pattern字符串其实相当于匹配内部规则,变成这样:pattern=r'([0-9]{4}year[0-9]{1,2}month[1-9]{1,2}day)'然后在括号外右边加上限定词,比如异常日期连续出现两次,这也是连续出现两次的规律,加上限定词{2},就可以了变成这样:pattern=r'([0-9]{4}year[0-9]{1,2}month[1-9]{1,2}days){2}'还没结束!连续出现两次的外部模式仍然需要用括号“包装”。上一节提醒你,外面连续出现的pattern是用“外括号”匹配的,最后pattern变成这样:pattern=r'(([0-9]{4}year[0-9]{1,2}month[1-9]{1,2}day){2})'来看看效果:pattern=r'(([0-9]{4}year[0-9]{1,2}month[1-9]{1,2}day){2})'message='今天是2020年3月1日2020年3月1日2019年7月17日2019年7月17日发生的事件记忆犹新'findall=re.findall(pattern,message)findallout:[('2020年03月1日2020年3月1日','2020年3月1日'),('July17,2019July17,2019','July17,2019')]这样就可以匹配所有具有内部正则和外部连续正则的字符串。更多行业干货持续为您分享,您可以随时关注我们!(1)获取更多优质内容,可以前往:当前疫情有所缓和,也是提升自我、为未来蓄力的好时机——蓄势待发!(2)搜索CDA小程序,手机端随时随地浏览最新资讯和优质课程:aA-zZ?