一、正则表达式简介
就其本质而言,正则表达式(或RE)是一种小型的、高度专业化的(在python中),它内嵌在python中,并通过RE模块实现。正则表达式编译成一系列字节码,然后由用C编写的匹配引擎执行。
可以用正则表达式测试工具进行测试》
二、字符匹配(普通字符、元字符)
普通字符:大多数字符和字母都会和自身匹配
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall('alex','faljfaljflajalexaa') print(li) #打印输出['alex']
元字符的作用:
. ----代表除了换行符以外的其他任何字符
^ ----代表以后面的字符开始
$ ----代表以前面的字符结束
[] -----字符集,[a-z]代表小写字母从a到z的任何一个字母,[0-9]代表0-9的任何一个数字,[.]代表字符.,[a9]代表a或9,[^a-z]代表除了a-z的其他任何字符
------反斜杠后面跟元字符,去掉特殊功能变为普通字符;后面跟普通字符实现特殊功能;
数字引用序号对应的字组所匹配的字符串,例如:
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.search(r'(alex)(eric)com2','alexericcomeric').group() print(li) #alexericcomeric,其中2代表的是第2组括号
d 匹配任何十进制数字,相当于[0-9]
D 匹配任何非数字字符,相当于[^0-9]
回车符( )、换行符( )、水平制表符( )、垂直制表符(v)、换页符(f)
s 匹配任何空白字符,相当于[ fv]
S 匹配任何非空白字符,相当于[^ fv]
w 匹配任何字母数字字符,相当于[0-9a-zA-Z_]
W 匹配任何非字母数字字符,相当于[^0-9a-zA-Z_]
匹配一个单词和边界,也就是单词和空格间的位置,作用:就是你在匹配整个单词的时候,如果不是整个单词就不匹配I,很多单词里都有I的,这时候用I就表示匹配整个单词I,而不是单词中包含的I.
例如:
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall(r'[a-z]+','how are you') print(li) #是字母与空格之间的边界
如下面例子,有张三和张三丰,想匹配张三就可以用边界匹配:
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall(r'张三','张三 张三丰 李四 李四光 张三杰 张三') print(li) #打印输出:['张三', '张三'],如果没有那么匹配出4个张三
| 表示或者,x|y表示匹配x或者y,例如:匹配IP地址
#!usr/bin/env python # -*- coding:utf-8 -*- #匹配IP地址 import re li = re.search(r'(([01]?d?d|2[0-4]d|25[0-5]).){3}([01]?d?d|2[0-4]d|25[0-5])','192.168.1.12').group() print(li)
元字符之量词:
* -----代表任意个字符,0-多个字符
+ -----代表1-多个字符
? ------代表0-1个字符
{} ------代表重复固定次数,如:{3}重复3次,{3,}大于等于3次,{3,5}重复3-5次,{,3}重复0-3次
三、贪婪模式与最少匹配模式(?)
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall('alex*','ajalexxxxxaa') print(li) #打印输出['alexxxxx'],匹配多个字符时通常默认为贪婪模式
数量元字符后面加上?可以变为最少匹配模式,例如:
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall('alex*?','ajalexxxxxaa') print(li) #打印输出['ale'],*后面加上?,切换为最少匹配模式
但当两边字符都能匹配上,中间加?,不会变为最少匹配模式
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall('ad+?b','a23b') print(li) #打印输出['a23b']
四、正则表达式re的函数
1、match函数(只能匹配开始的字符,并且只匹配一次)
用法:re.match(pattern,string,flags=0) ----pattern正则表达式字符串,string,正则表达式作用于的字符串,flags编译标志位,用于修改正则表达式的匹配方式,例如大小写、多行匹配等。主要有以下选项:
re.I IGNORECASE忽略大小写,使匹配的大小写不敏感
re.X VERBOSE忽略空格,可以为方便观看而做注释用。
re.M MULTILINE多行匹配,影响^和$
re.S DOTALL使.匹配换行符在内的所有字符,例如:
#!usr/bin/env python # -*- coding:utf-8 -*- import re li1 = re.findall('.','abc de') #['a', 'b', 'c', 'd', 'e'] li2 = re.findall('.','abc de',re.S) #['a', 'b', 'c', ' ', 'd', 'e'] print(li1,li2)
一旦匹配成功会返回一个match object对象,该对象的方法如下:
group() ---返回re匹配的字符串
start() ----返回匹配开始的位置
end() ------返回匹配结束的位置
sapan() -----返回匹配一个元组包含匹配(开始,结束)的位置
group() -----返回re整体匹配的字符串,可以一次输入多个组号
groups() -----返回一个元组,元组中包含所有分组结果
groupdict() -----返回一个字典,字典中包含所有的分组结果
1)group() 返回re整体匹配的字符串,相当于group(0)
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group() print(li) #打印输出123abc456
2) group(n,m) 返回元组,组号为n,m所匹配的字符串,如果组号不存在,则抛出IndexError异常
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(1,2) print(li) #打印输出('123', 'abc')
也可以group(n) 返回组号所匹配的字符,例如
#!usr/bin/env python # -*- coding:utf-8 -*- import re li0 = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(0) #123abc456 li1 = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(1) #123 li2 = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(2) #abc li3 = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(3) #456 print(li0,li1,li2,li3)
3)groups() 返回元组,将所有组号匹配到的字符串以元组的形式返回,通常groups不需要参数。
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').groups() print(li) #打印输出('123', 'abc', '456')
4)group,groups,groudict的用法及区别,如:下面三个例子:
#!usr/bin/env python # -*- coding:utf-8 -*- import re str = 'hello how are you!' r = re.match('hw+',str) print(r.group()) #打印输出:hello print(r.groups()) #打印输出:() print(r.groupdict()) #打印输出:{}
#!usr/bin/env python # -*- coding:utf-8 -*- import re str = 'hello how are you!' r = re.match('(h)(w+)',str) print(r.group()) #打印输出:hello print(r.groups()) #打印输出:('h', 'ello') print(r.groupdict()) #打印输出:{}
#!usr/bin/env python # -*- coding:utf-8 -*- import re str = 'hello how are you!' r = re.match('(?P<a1>h)(?P<a2>w+)',str) print(r.group()) #打印输出:hello print(r.groups()) #打印输出:('h', 'ello') print(r.groupdict()) #打印输出:{'a1': 'h', 'a2': 'ello'}
2、search函数(浏览全部字符串,匹配第一个符合规则的字符串)
参数与match完全相同
3、findall函数(匹配所有符合的字符,匹配多次) finditer()函数
findall,如果用到组,那么优先返回组内容,如果想返回所有内容,则需要在组前面加?:,例如:
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall('www.(baidu|laonanhai).com','www.baidu.com') print(li) #打印输出:['baidu']
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall('www.(?:baidu|laonanhai).com','www.baidu.com') print(li) #打印输出:['www.baidu.com']
findall函数返回一个列表,finditer返回的是一个迭代器
#!usr/bin/env python # -*- coding:utf-8 -*- import re iter_object = re.finditer('d','one1two2three3four4') print(iter_object) #<callable_iterator object at 0x000001CF23FBA8D0> for i in iter_object: print(i.group(),i.span()) #打印输出 # 1(3, 4) # 2(7, 8) # 3(13, 14) # 4(18, 19)
分组如果是量词为*则可以匹配到空字符,例如:
#!usr/bin/env python # -*- coding:utf-8 -*- import re n = re.findall('(dasd)*','lasd2asdp3asd98kif') print(n) #['', '', '', '', '2asd', '', '3asd', '', '', '', '', '', '']
import re n = re.findall('(dasd)+','lasd2asdp3asd98kif') #如果能多次匹配则显示最后一次匹配,lasd2asd匹配到的是2asd print(n) #['2asd', '3asd']
分组括号内部加":?"可以取消分组,例如:
#!usr/bin/env python # -*- coding:utf-8 -*- import re n = re.findall('(?:dasd)+','1asd2asdp3asd98kif') #分组内部加?:可以取消分组效果 print(n) #['1asd2asd', '3asd']
4、sub与subn函数
re.sub(pattern,repl,string,max=0)
参数:pattern正则表达式字符串,repl替换为的字符串,string要查找替换的字符窜,max=0 全部替换,1从左到右替换1个,2从左到右替换两个.......
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.sub('g.t','have','I get A,I got B,I gut C') print(li) #打印输出 have A,I have B,I have C
subn函数返回一个元组,元组中包含两个元素,第1个元素是替换后的字符串,第2个元素为替换次数,例如:
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.subn('g.t','have','I get A,I got B,I gut C') print(li) #打印输出 ('I have A,I have B,I have C', 3)
5、compile函数
re.compile(strPattern[,flag])
这个方法是pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象,第2个参数为编译标志位,可以利用,re.I等。
把常用的正则表达式编译为Pattern对象,可以被反复调用,从而提高效率。
#!usr/bin/env python # -*- coding:utf-8 -*- import re text = 'JGood is a hansome boy,he is cool,clever.' regex = re.compile(r'w*oow*') print(regex.findall(text)) #打印输出['JGood', 'cool']
6、split函数 ----分割函数
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.split(r'd','one1two2three3four4') print(li) #打印输出['one', 'two', 'three', 'four', '']
也可以结合copile函数:
#!usr/bin/env python # -*- coding:utf-8 -*- import re p = re.compile(r'd') li = p.split('one1two2three3four4') print(li) #打印输出['one', 'two', 'three', 'four', '']
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.split('[ab]','cabd') print(li) #['c', '', 'd'],注意空字符串是连续分割的时候产生的
split可以支持分组分隔,如下两个例子:
#!usr/bin/env python # -*- coding:utf-8 -*- import re st = 'hello alex bcd abcd lge acd 19' n = re.split('aw+',st,1) print(n) #['hello ', ' bcd abcd lge acd 19']
#!usr/bin/env python # -*- coding:utf-8 -*- import re st = 'hello alex bcd abcd lge acd 19' n = re.split('a(w+)',st,1) print(n) #['hello ', 'lex', ' bcd abcd lge acd 19']
五、关于rawstring(原生字符串)以及 符
------反斜杠后面跟元字符,去掉特殊功能变为普通字符;后面跟普通字符实现特殊功能;
python反斜杠中作用:
表示换行符, ASCII码是10
表示回车符,ASCII码是13
制表符
表示退格字符
#!usr/bin/env python # -*- coding:utf-8 -*- f = open(r"D:abc.txt")
如果去掉r用:
f = open(r"D:abc.txt")
则会报错,因为a表示特殊意义。
a是转义字符007,表示响铃符DEL
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall('\\','abcd') print(li)
分析一下“\\”,第一个斜杠是转义符,第二个斜杠是斜杠本身,第三个斜杠是转义符,第四个斜杠是斜杠本身。
有2点要清楚:
1.字符串里面表示斜杠就需要两个斜杠如“\”
2.正则表达式里的斜杠需要转意,是用“\”标示。
这样就比较好解释:
我们先要表示正则表达式里面的斜杠“\”,然后再用字符串表示出来。而这2个斜杠分别需要一个转义符,这样就成了4个斜杠在正则表达式里面表示 一个斜杠
如果用原生字符串r,两个双斜杠就可以达到目的:
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall(r'\','abcd') print(li)
第2个斜杠表示:python原生的字符串,传给正则后,正则用原生的进行转义。
import re R = re.match(r'blow','blow') #或者用\b,python字符串中表示退格,所以传给正则时并不是而是退格, ##所以python字符串中需要转义把原值传给正则表达式 #正则表达式把翻译为元字符单词边界 print(R)
六、其他及补充用法
1、分组,如下面数据,匹配abc一次货多次的
abcff
abcabcaadd
abcabcabc
re: (abc)+
2、|的用法
张三|二丰 ----表示匹配张三或二丰
张(三|二)丰 ----表示匹配张三丰或张二丰
3、后向引用与非捕获,分组的延续引用
后向引用:
前面日期是入职日期,后面日期是离职日期,找出当年入职当年离职的日期
正则表达式:(d{4}).+1.+
1代指(d{4}),并且与(d{4})匹配的值完全相同
在职日期
2009-9-10 2009-10-4
2008-9-10 2010-10-4
2003-9-10 2003-11-4
2001-3-10 2001-6-4
1998-3-4 2012-1-1
2012-10-1 2012-10-29
2005-3 2005-8
2004-2-13 2005-2-3
2001年2月到2001年9月
非捕获分组:分组默认的情况是有匹配组的,如果不想要匹配组那么可以在括号里面的最前面加?:,这样就可以去掉匹配组
例如:(?:d{4}).+1.+
4、[]的用法
[]表示括号中选中的若干字符之一
[xyz]字符集合,匹配包含的任意一个字符
[^xyz]负值字符集合。匹配未包含的任意一个字符
[a-z]字符范围,匹配制定范围内的任意字符
[^a-z]负值字符范围,匹配任何不在范围内的任意字符
[0-9]字符范围,匹配制定范围内的任意字符
[^0-9]负值字符范围,匹配任何不在范围内的任意字符
例如:匹配单个单词
How are you
正则表达式:[a-zA-Z]+
5、字符组去编号
正则表达式:d+-d+[、. ]?
1-11.判断语句之IF
1-12、判断语句IF之多条件
1-13判断语句之SELECT
1-14、循环语句之DO...LOOP
1-15、循环语句之DO...LOOP实例
1-16循环语句之DO WHILE...LOOP
1-2、宏在工作中的运用
1-3、Excel VBA基础
13-4、Excel VBA窗口介绍
1-6.对象
1-8、方法
341-5、Excel VBA代码编写规则
434-7、属性
81-9、常量、变量
6、首尾锚定
^ ----匹配字符串的开始位置
$ ----匹配字符串的结束位置
例如:以大写字母开头数字结尾的字符串
正则表达式:^[A-Z].*d$
7、汉字匹配
un 匹配 n,其中n是四位十六进制数表示的Unicode字符
汉字范围的第一个字符是“一”,其编码为:u4e00;最后一个字符为龢,其编码为:u9fa5
汉字范围为:[一-龢]
例如:提取字符串中的汉字
#!usr/bin/env python # -*- coding:utf-8 -*- import re li = re.findall('[一-龢]','苹果apple橘子orange') print(li) #打印输出:['苹', '果', '橘', '子']
8、零宽断言
正向零宽断言,从左到右查看匹配:(?=...)
负向零宽断言,与正向零宽断言相反,从左到右查看不匹配的位置:(?!...)
零宽断言实际匹配的是一个位置,并不是实际字符或字符串。
正则表达式:(?=I) ----匹配到I前面的位置
正则表达式:(!=I) ----除了I前面的位置,其他位置都匹配到了
例1:在参赛选手省份简称前面加(中)
正则表达式:(?=[川吉云粤])
参赛选手国籍 修改后
李四-川 李四-(中)川
陈升东-吉 陈升东-(中)吉
竹下君-日本 竹下君-日本
梁汉升-云 梁汉升-(中)云
张三-川 张三-(中)川
刘心兵-云 刘心兵-(中)云
龙心-粤 龙心-(中)粤
朴志熙-韩国 朴志熙-韩国
成龙国-粤 成龙国-(中)粤
例2:职务带总字的加上“高管”
正则表达式::(?=副?总) ---替换为:“:高管”
张三:总经理 张三:(高管)总经理
李四:车间主任 李四:车间主任
陆一:总裁 陆一:(高管)总裁
周同生:经理 周同生:经理
欧阳小小:副总经理 欧阳小小:(高管)副总经理
林汤圆:主管 林汤圆:主管
张山:副经理 张山:副经理
例3:提取下面字符串中的金额
正则表达式:d+.?d*(?=[元块])
买2件衣服:600元,买12袋零食:89.5元,打游戏:98.5元
买3本书:97块,买1双鞋子:408元,买日用品:389.7元
买化妆品:305元,买1辆单车:670元,买1支笔:8元
5个朋友过生日送礼物费用895.9元,买了1只小猫200块
日用品:200元,请客吃饭:590元,借给朋友2000块,丢了100元。
例4、负向零宽断言,给每个单子中间加-
正则表达式:(?!^)(?=[a-z])
pipe 管 p-i-p-e 管
9、懒惰与贪婪模式
一般元字符量词都是贪婪模式,如果想变为懒惰模式则在元字符量词后面加?
?的用法:
1)表示量词{0,1}
2)表示非捕获型的匹配模式(?:)
3)表示零宽断言(?=) (?!)
4)表示切换为懒惰模式:+?
例如:字符串abcdef
正则表达式:[a-z]+ 匹配结果:[abcdef]
正则表达式:[a-z]+? 匹配结果:[a,b,c,d,e,f]
10、分组的妙用
例1:下面字符串,提取部门和人数:
财务部 26人 业务部 4人 回收站 2人 人事科 34人 生产车间 4567人
#!usr/bin/env python # -*- coding:utf-8 -*- import re st = '财务部 26人 业务部 4人 回收站 2人 人事科 34人 生产车间 4567人' li = re.findall('([一-龢]+) (d+人)',st) print(li) #打印输出: #[('财务部', '26人'), ('业务部', '4人'), ('回收站', '2人'), ('人事科', '34人'), ('生产车间', '4567人')]
例2:下面字符串,提取姓名、身份证号、性别、年龄、籍贯
丁红梅 130981198206188284 女 30 河北省 沧州市 泊头市 蔚然 632324196704122182 女 45 青海省 黄南藏族自治州 河南蒙古族自治县 宓香菱 371103196505169263 女 47 山东省 日照市
#!usr/bin/env python # -*- coding:utf-8 -*- import re st = '丁红梅 130981198206188284 女 30 河北省 沧州市 泊头市 蔚然 632324196704122182 女 45 ' '青海省 黄南藏族自治州 河南蒙古族自治县 宓香菱 371103196505169263 女 47 山东省 日照市 ' li = re.findall('(S+) (S+) (S) (d+)(( S+){1,3})',st) print(li) #打印输出: #[[('丁红梅', '130981198206188284', '女', '30', ' 河北省 沧州市 泊头市', ' 泊头市'), # ('蔚然', '632324196704122182', '女', '45', ' 青海省 黄南藏族自治州 河南蒙古族自治县', ' 河南蒙古族自治县'), # ('宓香菱', '371103196505169263', '女', '47', ' 山东省 日照市', ' 日照市')]
例3:分组包含分组的情况
#!usr/bin/env python # -*- coding:utf-8 -*- import re st = 'hello alex alex bac abcd 19' r = re.findall('((a)(w+))',st) print(r) #[('alex', 'a', 'lex'), ('alex', 'a', 'lex'), ('ac', 'a', 'c'), ('abcd', 'a', 'bcd')]
例4:利用正则表达式计算:(参考split分组)
1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )的值
#!usr/bin/env python # -*- coding:utf-8 -*- import re def calculate(exp): return eval(exp) st = '1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )' while True: print(st) li = re.split('(([^()]+))',st,1) if len(li) == 3: # left = li[0] # middle = li[1] # right = li[2] left,middle,right = li #本句代码相当于上面的三句注销的代码 middle_result = calculate(middle) st = left + str(middle_result) + right else: result = calculate(st) print(result) break
#!usr/bin/env python # -*- coding:utf-8 -*- n1,n2 = 3,4 print(n1,n2) #3 4 li = [11,22,33] n1,n2,n3 = li print(n1,n2,n3) #11 22 33