- Python实践:Python文本处理之正则表达式
- 初级三板斧
- 进阶登堂入室
- Python实战
- 分割
- 分组
- 隋唐练习
- 参考资料
正则表达式是一种用来匹配字符串的强有力的武器,利用 字符来匹配字符的思想,基于显示规则进行模式匹配,可以高效组合成不同样式的字符串,迅速处理字符串。
相信我,学会正则表达式,你在以后的文本处理时,一定会感谢我。温馨提示,建议配合《廖雪峰的Python正则表达式教程》研读,见文末参考资料1。
初级三板斧基础元字符意义总结如下,日常情况都是以下举例的组合:
-
d,表示一个数字
-
w,表示一个字母或数字
-
. , 表示任意一个字符,如:'py.‘可以匹配’pyc’、‘pyo’、'py!'等
-
*, 表示任意个字符(包括0个),对前一个字符模式重复任意次
-
+, 表示至少一个字符
-
?, 表示0个或1个字符
-
{}, 表示自定义个字符个数,修饰前面一个变量最长的字符个数,如{n}表示n个字符;{n,m}表示n-m个字符,可取到m个字符
-
s, 表示一个空白符,如空格Space、Tab等,s+可表示至少有一个空格
组合示例:
- d{3,8} ,表示3-8个数字组成的字符串,例如’123’或’12345678’
- d{3},表示3个数字,例如’010’
- w.,表示任意一个字符(字符或数字)配一个任意字符, 如’a.', ‘p!’
- [A-Z], 表示可能有0或1个<符号,后面接一个大写字母,如‘
- ``s+`,表示至少有一个空白符,如’ ', ’ ’
高频问题总结如下:
-
转义问题,标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。
- 如:'-_?.',都是特殊字符,在正则表达式中,要用转义
- 如:r'py.',才能查找py.这样的字符串
-
|,选择分支表示或的关系,A|B可以匹配A或B,如(P|p)ython可以匹配’Python’或者’python’。
-
[],表示当前一个字符的范围,大于两种的可能;如:[a-zA-Z_][0-9a-zA-Z_]{0, 19},前面1个字符+后面最多19个字符
-
^,表示行的开头,如^d,表示必须以数字开头。
-
$,表示行的结束,如d$,表示必须以数字结束。
-
(),分组功能,(xyz) 字符组,按照确切的顺序匹配字符 xyz。
-
[^],否定字符类。匹配方括号中不包含的任意字符
组合示例:
- ^(P|p)y,表示首行以P或p开头,第二个字符为y的字符串
- ^py&,表示整行匹配,行首以p开头,且中间无字符,行尾以y结束
- [^w]+ ,表示所有非字母或数字的字符串
Python用re模块来使用正则表达式功能,原因是正则表达式的英文为“Regular Expression”,常见缩写为:regex 或 regexp。
import re # 导入正则表达式模块
由于Python的字符串本身也用转义,所以一般使用时要对本身进行转义,变成\。比如:
s = 'ABC\-001' # Python的字符串 # 对应的正则表达式字符串变成: # 'ABC-001'
但还有个更优雅的解决办法,强烈推荐:
- 在字符串前加r,表示按字面意思处理,不转义,可达到相同效果
- 当然,此时n,t,%d等转义字符就无转义功能了
s = r'ABC-001' # Python的字符串分割
根据re的split()函数,基于正则表达式的Python字符串分割,说明如下:
- re.split(r'pattern', 'str')
- 入参1:匹配pattern,入参2:带查找字符串
实例如下:
# 功能:去除空格等空白符,+表示以上形式的字符至少有一个 re.split(r's+', 'a b c') # 功能:去除空白符和逗号,+表示以上形式的字符至少有一个 re.split(r'[s,]+', 'a,b, c d')分组
主要知识点:
- groups()函数
- ()的用法,每个()对应分离出一组结果
再以分离识别时间字符串例子进行说明。
t = '19:05:30'
m = re.match(r'^(0[0-9]|1[0-9]|2[0-3]|[0-9]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$', t)
m.groups() # >>> ('19', '05', '30')
分段理解:
- (0[0-9]|1[0-9]|2[0-3]|[0-9]),表示识别第一个组,即’19:05:30’中的19
- 第一组,总体有4种可能形式,用逻辑或|连接
- 0*
- 1*
- 2*
- *(一位数表示场景)
- 0[0-9],场景1,表示0字符开头,加一个0-9数字, 00-09
- 1[0-9],场景2,10-19
- 2[0-3],场景3,20-23,注意没有24点,因为有00:00
- [0-9], 场景4,0-9,处理仅一位数的场景
此外,要注意贪心匹配法的问题,以下内容摘自廖雪峰正则表达式文章。
# 默认贪心匹配,即大嘴法,能跟更多的满足条件的组合
>>> re.match(r'^(d+)(0*)$', '102300').groups()
>>> ('102300', '')
# 必须让d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0匹配出来,加个?就可以让d+采用非贪婪匹配:
>>> re.match(r'^(d+?)(0*)$', '102300').groups()
>>> ('1023', '00')
行文至此,细心的读者可能发现,一直都是在处理英文字符,如果要查找中文样式的字符如何处理呢?
正则表达式匹配汉字核心思路是:在unicode编码模式下,汉字编码范围:u4e00-u9fa5,可据此进行表征,详细内容见:此文。
隋唐练习来自廖雪峰文章的作业,根据以上知识试解决以下两个问题。
- 问题1:一个验证Email地址的正则表达式。如以下Email格式:someone@gmail.com;bill.gates@microsoft.com
- 问题2:提取出带名字的Email地址。如:
tom@voyager.org => Tom Paris;bob@example.com => bob
参考代码及详解如下:
def is_valid_email(addr):
# [w.]+ 表示至少有任意一个字母或者数字或者.出现,多个类似数字依然认可
# 直到遇到@
# w+ 表示只有字符(字母或数字)出现
# 直到遇到.com$一直到结尾
# re_email = re.compile(r'[w.]+@w+.com$') # 将正则表达式预编译,以便经常调用
# if re_email.match(addr):
if re.match(r'[w.]+@w+.com$', addr):
return True
else:
return False
def name_of_email(addr):
# ?', addr)
return res.group(1) # group是个函数,1是入参,表示分组后的第一个结果,0则表示分组前的内容
参考资料
- 廖雪峰正则表达式博客,讲解清晰,简明易懂,入门必备:link
- 好心人总结的进阶补充手册,概况全面:link
- 菜鸟教程,体系化总结:link



