Python笔记之 正则表达式
动机
- 文本处理已经成为计算机常见工作之一
- 对文本内容的搜索、定位、提取是逻辑比较复杂的工作
- 为了快速方便的解决上述问题,产生了正则表达式技术
简介
- 定义
即文本的高级匹配模式,提供搜索、替换等功能。其本质是由一系列字符和特殊符号构成的字串。这个字串即正则表达式
- 原理
通过普通字符和有特定含义的字符,来组成字符串,用以描述一定的字符串规则,比如:重复、位置等,来表达某类特定的字符串,进而匹配
元字符
普通字符
除了元字符以外的所有UTF-8字符
- 匹配规则:每个普通字符匹配其对应的字符
In [1]: import re In [2]: re.findall('ab','abcdefgab') Out[2]: ['ab', 'ab']
- 注意事项:正则表达式在Python中也可以匹配中文
In [3]: re.findall('傻子','傻子二傻子') Out[3]: ['傻子', '傻子']
或关系
- 元字符:|
- 匹配规则:匹配 | 两端任意的正则表达式即可
In [4]: re.findall('傻子|疯子','傻子和疯子,傻子和二傻子')
Out[4]: ['傻子', '疯子', '傻子', '傻子']
注意!不要在|两边加空格,比如'a | b'会匹配'a '和' b'而不是'a'和'b'
匹配单个字符
- 元字符:.
- 匹配规则:匹配除换行外的任意一个字符
In [5]: re.findall('张.丰','张三丰,张四丰')
Out[5]: ['张三丰', '张四丰']
匹配字符集
- 元字符:[字符集]
- 匹配规则:匹配字符集中的任意一个字符。
- 表达形式:
[abc#!好]表示[]中的任意一个字符
[0-9],[a-z],[A-Z]表示匹配区间内任意一个字符
[_#?0-9a-z]混合书写,一般区间表达写在后面
In [6]: re.findall('[aeiou]','Hello world!Hello re!')
Out[6]: ['e', 'o', 'o', 'e', 'o', 'e']
In [7]: re.findall('[aeiou][aeiou]','How are you?')
Out[7]: ['ou']
In [8]: re.findall('[0-9]','100')
Out[8]: ['1', '0', '0']
In [9]: re.findall('[-0-9a-z]','Hi,007.Hi,B-J')
Out[9]: ['i', '0', '0', '7', 'i', '-']
注意!每次只能匹配一个字符!
匹配字符集反集
- 元字符:[^字符集]
- 匹配规则:匹配除了字符集以外的任意一个字符
In [10]: re.findall('[^0-9]','Use 007 port')
Out[10]: ['U', 's', 'e', ' ', ' ', 'p', 'o', 'r', 't']
匹配字符串开始位置
- 元字符:^
- 匹配规则:匹配目标字符串的开头位置
In [11]: re.findall('^Jim','Jim,Jim,Jim')
Out[11]: ['Jim'] # 此时只会匹配开头的Jim,而不是所有的Jim
In [12]: re.findall('^Jim','Hello,Jim')
Out[12]: [] # 因为Jim不在开头,所以什么也没匹配到
匹配字符串的结尾位置
- 元字符:$
- 匹配规则:匹配目标字符串的结尾位置
In [13]: re.findall('Jim$','Hello,Jim')
Out[13]: ['Jim']
In [14]: re.findall('^Jim$','Jim,Jim')
Out[14]: []
In [15]: re.findall('^Jim$','JimJim')
Out[15]: []
In [16]: re.findall('^Jim$','Jim')
Out[16]: ['Jim']
In [17]: re.findall('^Jim$','Hello,Jim')
Out[17]: []
匹配字符重复
- 元字符:*
- 匹配规则:匹配前面的字符出现0次或多次
In [18]: re.findall('wo*','woooooo~~w!')
Out[18]: ['woooooo', 'w']
In [19]: re.findall('o*','woooooo~~w!')
Out[19]: ['', 'oooooo', '', '', '', '', '']
In [20]: re.findall('[A-Z][a-zA-Z]*','Hello World wow Abc Lucy,what')
Out[20]: ['Hello', 'World', 'Abc', 'Lucy']
- 元字符:+
- 匹配规则:匹配前面的字符出现1次或多次
In [21]: re.findall('[A-Z][a-zA-Z]+','Hello world')
Out[21]: ['Hello']
In [22]: re.findall('[A-Z][a-zA-Z]+','Hello H')
Out[22]: ['Hello']
- 元字符:?
- 匹配规则:匹配前面的字符出现0次或1次
In [23]: re.findall('[A-Z][a-zA-Z]?','Hello He')
Out[23]: ['He', 'He']
In [24]: re.findall('-?[0-9]+','19 25 -690 -14')
Out[24]: ['19', '25', '-690', '-14']
- 元字符:{n}
- 匹配规则:匹配前面的字符出现n次
In [25]: re.findall('1[0-9]{10}','Jame:13886495728')
Out[25]: ['13886495728']
- 元字符:{m,n} (m<n)
- 匹配规则:匹配前面的字符出现m到n次
In [26]: re.findall('[1-9][0-9]{5,10}','Baron:1256752332,Amy:123')
Out[26]: ['1256752332']
匹配任意(非)数字字符
- 元字符:\d \D
- 匹配规则:\d 匹配任意数字字符,\D 匹配任意非数字字符
In [27]: re.findall('\d{1,5}','Mysql:3306,http:80')
Out[27]: ['3306', '80']
In [28]: re.findall('\D{1,5}','Mysql:3306,http:80')
Out[28]: ['Mysql', ':', ',http', ':']
匹配任意(非)普通字符
- 元字符:\w \W
- 匹配规则:\w 匹配普通字符,\W 匹配非普通字符
- 说明:普通字符指数字、字母、下划线,汉字
In [29]: re.findall('\w+','server_port = 8888')
Out[29]: ['server_port', '8888']
In [30]In [30]: re.findall('\w+','你好 = あつぬのつㅙㅝㄲㄴㅓДЁУΕΔγπ')
Out[30]: ['你好', 'あつぬのつㅙㅝㄲㄴㅓДЁУΕΔγπ']
In [31]: re.findall('\W','test.py')
Out[31]: ['.']
匹配任意(非)空字符
- 元字符:\s \S
- 匹配规则:\s 匹配空字符,\S匹配非空字符
- 说明:空字符指 空格 \r \n \t \v \f字符
In [32]: re.findall('\w+\s+\w+','hello world')
Out[32]: ['hello world']
In [33]: re.findall('\S+','BRB-1907%wq#_ hhhhh')
Out[33]: ['BRB-1907%wq#_', 'hhhhh']
匹配开头结尾位置
- 元字符:\A \Z
- 匹配规则:\A表示开头位置,\Z表示结尾位置
匹配(非)单词的边界位置
- 元字符:\b \B
- 匹配规则:\b 表示单词边界,\B 表示非单词边界
- 说明:单词边界指数字字母(汉字)下划线与其他字符的交界位置
In [34]: re.findall('is','This is a test')
Out[34]: ['is', 'is']
In [35]: re.findall('\bis\b','This is a test')
Out[35]: []
In [36]: re.findall(r'\bis\b','This is a test')
Out[36]: ['is']
类别 | 元字符 |
---|---|
匹配字符 | . [...] [^...] \d \D \w \W \s \S |
匹配重复 | * + ? {n} |
匹配位置 | ^ $ \A \Z \b \B |
其他 | | () \ |
正则表达式的转义
- 如果使用正则表达式匹配特殊字符则需要加 \ 表示转义
特殊字符:. * + ? ^ $ [] () {} | \
In [37]: re.findall('-?[0-9]+\.*[0-9]+','12 1.45 -690 -5.6') Out[37]: ['12', '1.45', '-690', '-5.6'] In [38]: re.findall('-?\d+\.*\d+','12 1.45 -690 -5.6') Out[38]: ['12', '1.45', '-690', '-5.6']
- 在编程语言中,常使用原生字符串书写正则表达式避免多重转义的麻烦
'\\$\\d+' 等同于 r'\$\d+'
贪婪模式和非贪婪模式
-
定义
贪婪模式:默认情况下,匹配重复的元字符总是尽可能多的向后匹配内容。如:* + ?
非贪婪模式(懒惰模式):让匹配重复的元字符尽可能少的向后匹配字符
-
贪婪模式转换为非贪婪模式
+ : +? * : *? ? : ?? {m,n} : {m,n}?
In [39]: s = '''吴京,1974年4月3日出生于北京市,毕业于北京体育大学,中国影视男演员、电影导演,中国电影家协会副主席。 [1 ...: ] ...: 1989年进入北京市武术队。1994年获得全国武术比赛精英赛枪术、对练冠军。1995年出演个人首部电影《功夫小子闯情关》, ...: 从而进入演艺圈。1998年因在古装剧《太极宗师》中饰演杨昱乾一角而被观众熟知。1999年主演武侠剧《小李飞刀》 [2] 。 ...: ...: 2003年赴香港发展电影事业。2005年参演动作片《杀破狼》并首次尝试反派角色。2007年,凭借动作警匪片《男儿本色》获得 ...: 第44届台湾电影金马奖最佳男配角提名 [3] 。2008年开始转型担任导演,并于同年执导导演处女作《狼牙》。 ...: 2012年在军旅剧《我是特种兵之利刃出鞘》中首度饰演军人 [4] 。2015年主演犯罪悬疑片《杀破狼2》;同年,自编自导自 ...: 演军事战争片《战狼》,并凭借该片获得第33届大众电影百花奖最佳导演提名、第22届北京大学生电影节最佳处女作奖、第20 ...: 届华鼎奖最佳新锐导演奖 [5] 。2017年自导自演动作片《战狼Ⅱ》,该片创下国产电影历史最高票房记录及全球单一市场单 ...: 片最高票房记录 [6-7] ,而其个人则凭借该片先后获得第34届大众电影百花奖最佳男主角奖 [8] 、第17届中国电影华表奖 ...: 优秀男演员奖 [9] 。2019年,担任科幻电影《流浪地球》的出品人,并特别出演刘培强一角 [10-11] 。 ...: 2020年02月,演唱文艺界抗击疫情主题MV《坚信爱会赢》。 [12] 2020年7月,奥斯卡主办方美国电影艺术与科学学会今年招 ...: 新名单出炉,吴京在列''' In [40]: s Out[40]: '吴京,1974年4月3日出生于北京市,毕业于北京体育大学,中国影视男演员、电影导演,中国电影家协会副主席。 [1] \n1989年进入北京市武术队。1994年获得全国武术比赛精英赛枪术、对练冠军。1995年出演个人首部电影《功夫小子闯情关》,从而进入演艺 圈。1998年因在古装剧《太极宗师》中饰演杨昱乾一角而被观众熟知。1999年主演武侠剧《小李飞刀》 [2] 。\n2003年赴香港发展电影事业。2005年参演动作片《杀破狼》并首次尝试反派角色。2007年,凭借动作警匪片《男儿本色》获得第44届台湾电影金马奖最佳男配角提名 [3] 。2008年开始转型担任导演,并于同年执导导演处女作《狼牙》。\n2012年在军旅剧《我是特种兵之利刃出鞘》中首度饰演军人 [4] 。2015年主演犯罪悬疑片《杀破狼2》;同年,自编自导自演军事战争片《战狼》,并凭借该片获得第33届大众电影百花奖最佳 导演提名、第22届北京大学生电影节最佳处女作奖、第20届华鼎奖最佳新锐导演奖 [5] 。2017年自导自演动作片《战狼Ⅱ》,该片创下国产电影历史最高票房记录及全球单一市场单片最高票房记录 [6-7] ,而其个人则凭借该片先后获得第34届大众电影百花奖最佳男主角奖 [8] 、第17届中国电影华表奖优秀男演员奖 [9] 。2019年,担任科幻电影《流浪地球》的出品人,并特别出演刘培强一角 [10-11] 。\n2020年02月,演唱文艺界抗击疫情主题MV《坚信爱会赢》。 [12] 2020年7月,奥斯卡主办方美国电影艺术与科学学会今年招新名单出炉,吴京在列' In [41]: re.findall(r'《.+》',s) # 贪婪模式 Out[41]: ['《功夫小子闯情关》,从而进入演艺圈。1998年因在古装剧《太极宗师》中饰演杨昱乾一角而被观众熟知。1999年主演武侠剧《小李飞刀》', '《杀破狼》并首次尝试反派角色。2007年,凭借动作警匪片《男儿本色》获得第44届台湾电影金马奖最佳男配角提名 [3] 。2008年开始转型担任导演,并于同年执导导演处女作《狼牙》', '《我是特种兵之利刃出鞘》中首度饰演军人 [4] 。2015年主演犯罪悬疑片《杀破狼2》;同年,自编自导自演军事战争片《战狼》, 并凭借该片获得第33届大众电影百花奖最佳导演提名、第22届北京大学生电影节最佳处女作奖、第20届华鼎奖最佳新锐导演奖 [5] 。2017年自导自演动作片《战狼Ⅱ》,该片创下国产电影历史最高票房记录及全球单一市场单片最高票房记录 [6-7] ,而其个人则凭借该片先后获得第34届大众电影百花奖最佳男主角奖 [8] 、第17届中国电影华表奖优秀男演员奖 [9] 。2019年,担任科幻电影《流浪地球》', '《坚信爱会赢》'] In [42]: re.findall(r'《.+?》',s) # 懒惰模式 Out[42]: ['《功夫小子闯情关》', '《太极宗师》', '《小李飞刀》', '《杀破狼》', '《男儿本色》', '《狼牙》', '《我是特种兵之利刃出鞘》', '《杀破狼2》', '《战狼》', '《战狼Ⅱ》', '《流浪地球》', '《坚信爱会赢》']
正则表达式分组
- 定义
在正则表达式中,以()建立正则表达式的内部分组,子组是正则表达式的一部分,可以作为内部整体操作对象
- 作用
- 可以被作为整体操作,改变元字符的操作对象
e.g. 改变 +号 重复对象 In [43]: re.findall('ab+','abababababababababab') Out[43]: ['ab', 'ab', 'ab', 'ab', 'ab', 'ab', 'ab', 'ab', 'ab', 'ab'] In [44]: re.search('(ab)+','abababababababababab') Out[44]: <re.Match object; span=(0, 20), match='abababababababababab'> In [45]: re.search('(ab)+','abababababababababab').group() Out[45]: 'abababababababababab' In [46]: re.findall('(ab)+','abababababababababab') Out[46]: ['ab'] e.g. 改变 |号 操作对象 In [47]: re.search('(王|李)\w{1,3}','王者荣耀').group() Out[47]: '王者荣耀' In [48]: re.search('王|李\w{1,3}','王者荣耀').group() Out[48]: '王'
- 可以通过编程语言某些接口获取匹配内容中,子组对应的内容部分
In [49]: re.search(r'(http|https|ftp|file)://\S+','https://www.baidu.com').group(1) Out[49]: 'https' In [50]: re.search(r'(http|https|ftp|file)://\S+','https://www.baidu.com').group() Out[50]: 'https://www.baidu.com'
- 可以被作为整体操作,改变元字符的操作对象
- 捕获组
可以给正则表达式的子组起一个名字,表达该子组的意义。这种有名字的子组即为捕获组格式:(?P<name>pattern)
e.g. 给子组命名为pig In [51]: re.search(r'(?P<pig>ab)+','ababababababab').group('pig') Out[51]: 'ab'
- 注意事项
- 一个正则表达式中可以包含多个子组
- 子组可以嵌套,但是不要重叠或者嵌套结构复杂
- 子组序列号一般从外到内,从左往右计数
正则表达式匹配原则
- 正确性:能够正确的匹配出目标字符串
- 排他性:除了目标字符串之外尽可能少的匹配其他内容
- 全面性:尽可能考虑到目标字符串的所有情况,不遗漏
Python re模块的使用
- 常量、属性
-
re.A(re.ASCII)
让\w,\W,\b,\B,\d,\D,\s和\S 执行ASCII-只匹配完整的Unicode匹配代替。这仅对Unicode模式有意义,而对于字节模式则忽略。
-
re.I(re.IGNORECASE)
执行不区分大小写的匹配;类似的表达式也[A-Z]将匹配小写字母。
-
re.L(re.LOCALE)
让\w,\W,\b,\B和区分大小写的匹配取决于当前的语言环境。该标志只能与字节模式一起使用。不建议使用此标志,因为语言环境机制非常不可靠,它一次只能处理一种“区域性”,并且仅适用于8位语言环境。默认情况下,Python 3中已为Unicode(str)模式启用了Unicode匹配,并且能够处理不同的语言环境/语言。
-
re.M(re.MULTILINE)
指定时,模式字符'^'在字符串的开头和每行的开头(紧随每个换行符之后)匹配;模式字符''在字符串的末尾和每行的末尾(紧接在每个换行符之前)匹配。默认情况下,'^' 仅在字符串的开头,字符串''的末尾和字符串末尾的换行符(如果有)之前立即匹配。
-
re.S(re.DOTALL)
使'.'特殊字符与任何字符都匹配,包括换行符;没有此标志,'.'将匹配除换行符以外的任何内容
-
- 常用方法
- re.compile(pattern,flags = 0)
将正则表达式模式编译为正则表达式对象,可使用match(),search()以及下面所述的其他方法将其用于匹配
>>> prog = re.compile('\d{2}') # 正则对象 >>> prog.search('12abc') <_sre.SRE_Match object; span=(0, 2), match='12'> >>> prog.search('12abc').group() # 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。 '12' >>> prog.match('123abc') <_sre.SRE_Match object; span=(0, 2), match='12'> >>> prog.match('123abc').group() '12' >>>
- re.search(pattern,string,flags = 0)
扫描字符串以查找正则表达式模式产生匹配项的第一个位置 ,然后返回相应的match对象。None如果字符串中没有位置与模式匹配,则返回;否则返回false。请注意,这与在字符串中的某个点找到零长度匹配不同。
#在这个字符串进行匹配,只会匹配一个对象 >>> re.search('\w+','abcde').group() 'abcde' >>> re.search('a','abcde').group() 'a' >>>
- re.match(pattern,string,flags = 0)
如果字符串开头的零个或多个字符与正则表达式模式匹配,则返回相应的匹配对象。None如果字符串与模式不匹配,则返回;否则返回false。请注意,这与零长度匹配不同。
# 同search,不过在字符串开始处进行匹配,只会匹配一个对象 >>> re.match('a','abcade').group() 'a' >>> re.match('\w+','abc123de').group() 'abc123de' >>> re.match('\D+','abc123de').group() #非数字 'abc' >>>
- re.fullmatch(pattern,string,flags = 0)
如果整个字符串与正则表达式模式匹配,则返回相应的match对象。None如果字符串与模式不匹配,则返回;否则返回false。请注意,这与零长度匹配不同。
>>> re.fullmatch('\w+','abcade').group() 'abcade' >>> re.fullmatch('abcade','abcade').group() 'abcade' >>>
- re.split(pattern,string,maxsplit = 0,flags = 0)
通过出现模式来拆分字符串。如果在pattern中使用了捕获括号,那么模式中所有组的文本也将作为结果列表的一部分返回。如果maxsplit不为零,则最多会发生maxsplit分割,并将字符串的其余部分作为列表的最后一个元素返回。
如果分隔符中有捕获组,并且该匹配组在字符串的开头匹配,则结果将从空字符串开始。字符串的末尾也是如此:>>> re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割 ['', '', 'cd'] >>> re.split(r'\W+', 'Words, words, words.') ['Words', 'words', 'words', ''] >>> re.split(r'(\W+)', 'Words, words, words.') ['Words', ', ', 'words', ', ', 'words', '.', ''] >>> re.split(r'\W+', 'Words, words, words.', 1) ['Words', 'words, words.'] >>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE) ['0', '3', '9']
>>> re.split(r'(\W+)', '...words, words...') ['', '...', 'words', ', ', 'words', '...', '']
- re.findall(pattern,string,flags = 0 )
以string列表形式返回string中pattern的所有非重叠匹配项。从左到右扫描该字符串,并以找到的顺序返回匹配项。如果该模式中存在一个或多个组,则返回一个组列表;否则,返回一个列表。如果模式包含多个组,则这将是一个元组列表。空匹配项包含在结果中。
- re.compile(pattern,flags = 0)