Python——re模块
re模块
正则表达式就是字符串的匹配规则,在多数编程语言里都有相应的支持,python里对应的模块是re。
元字符 |
匹配内容 |
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结尾 |
\W |
匹配非字母或数字或下划线 |
\D |
匹配非数字 |
\S |
匹配非空白符 |
a|b |
匹配字符a或字符b |
() |
匹配括号内的表达式,也表示一个组 |
[...] |
匹配字符组中的字符 |
[^...] |
匹配除了字符组中字符的所有字符 |
量词:
量词 |
用法说明 |
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
字符组 : [字符组]
在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等。
假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一。
正则 |
待匹配字符 |
匹配 |
说明 |
[0123456789] |
8 |
True |
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符 |
[0123456789] |
a |
False |
由于字符组中没有"a"字符,所以不能匹配 |
[0-9] |
7 |
True |
也可以用-表示范围,[0-9]就和[0123456789]是一个意思 |
[a-z] |
s |
True |
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示 |
[A-Z] |
B |
True |
[A-Z]就表示所有的大写字母 |
[0-9a-fA-F] |
e |
True |
可以匹配数字,大小写形式的a~f,用来验证十六进制字符 |
匹配开头和结尾
>>> s = 'xyzd2asd' >>> re.search('^xy',s) <_sre.SRE_Match object; span=(0, 2), match='xy'> >>> re.search('sd$',s) <_sre.SRE_Match object; span=(6, 8), match='sd'> >>> re.search('y$',s) >>>
转义符 \
在正则表达式中,有很多有特殊意义的是元字符,比如\d和\s等,如果要在正则中匹配正常的"\d"而不是"数字"就需要对"\"进行转义,变成'\\'。
在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。
所以如果匹配一次"\d",字符串中要写成'\\d',那么正则里就要写成"\\\\d",这样就太麻烦了。这个时候我们就用到了r'\d'这个概念,此时的正则是r'\\d'就可以了
正则 | 待匹配字符 | 匹配 结果 |
说明 |
\d | \d | False |
因为在正则表达式中\是有特殊意义的字符,所以要匹配\d本身,用表达式\d无法匹配 |
\\d | \d | True |
转义\之后变成\\,即可匹配 |
"\\\\d" | '\\d' | True |
如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次 |
r'\\d' | r'\d' | True |
在字符串之前加r,让整个字符串不转义 |
贪婪与非贪婪模式
正则表达式通常用于在文本中查找匹配的字符串。Python里数量词默认是贪婪的,总是尝试匹配尽可能多的字符;
非贪婪则相反,总是尝试匹配尽可能少的字符。在"*","?","+","{m,n}"后面加上?,使贪婪变成非贪婪。
>>> s = '<html>i love python</html>' >>> re.findall('<html>.*?',s) ['<html>'] >>> re.findall('<html>.*',s) ['<html>i love python</html>']
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复 +? 重复1次或更多次,但尽可能少重复 ?? 重复0次或1次,但尽可能少重复 {n,m}? 重复n到m次,但尽可能少重复 {n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符 * 是取 0 至 无限长度 ? 是非贪婪模式。 在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在: .*?x 就是取前面任意长度的字符,直到一个x出现
Flags标志符
flags有很多可选值:
re.I(IGNORECASE)忽略大小写,括号内是完整的写法
re.M(MULTILINE)多行模式,改变^和$的行为
re.S(DOTALL)点可以匹配任意字符,包括换行符
re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释
>>> s1 = '<html>i love python</HTML>' >>> re.findall('ht',s1,re.I) ['ht', 'HT'] >>> re.findall('ht',s1) ['ht']
re模块中常用功能函数
1、compile()
编译正则表达式模式,返回一个对象的模式。(可以把那些常用的正则表达式编译成正则表达式对象,这样可以提高一点效率。)
格式:
re.compile(pattern,flags=0)
pattern: 编译时用的表达式字符串。
>>> import re >>> s = 'woxi is a good girl,she is cool,clever,and so on' >>> com_str = re.compile(r'g[a-z]{0,3}') >>> com_str.findall(s)
['good', 'girl']
2、match()
决定RE是否在字符串刚开始的位置匹配。//注:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符'$'
格式:
re.match(pattern, string, flags=0)
>>> print(re.match('com','comwww').group()) com >>> print(re.match('com','Comwww',re.I).group()) Com
3、search()
格式:
re.search(pattern, string, flags=0)
re.search函数会在字符串内查找模式匹配,只要找到第一个匹配然后返回,如果字符串没有匹配,则返回None。
>>> print(re.match('com','comwww').group()) com
*注:match和search一旦匹配成功,就是一个match object对象,而match object对象有以下方法:
- group() 返回被 RE 匹配的字符串
- start() 返回匹配开始的位置
- end() 返回匹配结束的位置
- span() 返回一个元组包含匹配 (开始,结束) 的位置
- group() 返回re整体匹配的字符串,可以一次输入多个组号,对应组号匹配的字符串。
a. group()返回re整体匹配的字符串,
b. group (n,m) 返回组号为n,m所匹配的字符串,如果组号不存在,则返回indexError异常
c.groups()groups() 方法返回一个包含正则表达式中所有小组字符串的元组,从 1 到所含的小组号,通常groups()不需要参数,返回一个元组,元组中的元就是正则表达式中定义的组。
import re a = "123abc456" print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).groups()) #('123', 'abc', '456'),返回匹配的所有分组 print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)) #123abc456,返回整体 print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)) #123 print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2)) #abc print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3)) #456 ###group(1) 列出第一个括号匹配部分,group(2) 列出第二个括号匹配部分,group(3) 列出第三个括号匹配部分。
4、findall()
re.findall遍历匹配,可以获取字符串中所有匹配的字符串,返回一个列表。
格式:
re.findall(pattern, string, flags=0)
s= re.compile(r'\d+') print(s.findall('o1w2a3')) # 执行结果如下: ['1', '2', '3'] import re t = "Tina is a good girl, she is cool, clever, and so on..." r = re.compile(r'\w*oo\w*') print(r.findall(t)) print(re.findall(r'(\w)*oo(\w)',t))#()表示子表达式 # 执行结果如下: ['good', 'cool'] [('g', 'd'), ('c', 'l')]
5、finditer()
搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。找到 RE 匹配的所有子串,并把它们作为一个迭代器返回。
格式:re.finditer(pattern, string, flags=0)
iter = re.finditer(r'\d+','12 aa33bb,11cc10dd') for i in iter: print(i) print(i.group()) print(i.span()) # <_sre.SRE_Match object; span=(0, 2), match='12'> # 12 # (0, 2) # <_sre.SRE_Match object; span=(5, 7), match='33'> # 33 # (5, 7) # <_sre.SRE_Match object; span=(10, 12), match='11'> # 11 # (10, 12) # <_sre.SRE_Match object; span=(14, 16), match='10'> # 10 # (14, 16)
6、split()
按照能够匹配的子串将string分割后返回列表。
可以使用re.split来分割字符串,如:re.split(r'\s+', text);将字符串按空格分割成一个单词列表。
格式:
re.split(pattern, string[, maxsplit])
maxsplit用于指定最大分割次数,不指定将全部分割。
print(re.split('\d+','one1two2three3four4five5')) 执行结果如下: ['one', 'two', 'three', 'four', 'five', '']
7、sub()
使用re替换string中每一个匹配的子串后返回替换后的字符串。
格式:
re.sub(pattern, repl, string, count)
import re text = "shuke is a handsome boy, he is cool, clever, and so on..." print(re.sub(r'\s+', ':', text)) # 执行结果如下: #JGood:is:a:handsome:boy,:he:is:cool,:clever,:and:so:on... # 其中第二个函数是替换后的字符串;本例中为':' # 第四个参数指替换个数。默认为0,表示每个匹配项都替换。
re.sub还允许使用函数对匹配项的替换进行复杂的处理。
import re s = '1267' def replace_digit(m): ss = '零一二三四五六七八九' index = int(m.group()) return ss[index] result = re.sub(u'\d', replace_digit, s, count=4) print(result) # 一二六七
re模块的一些小应用
匹配中文
>>> s1='ss我们必须成为大海aabbb' >>> re.findall('[\u4e00-\u9fa5]+',s1) ['我们必须成为大海'] >>>
匹配IP
((1\d{2}|25[0-5]|2[0-4]\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)
import re right=r'((1\d{2}|25[0-5]|2[0-4]\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)' n=re.search(right,'192.168.1.5') print(n.group(0)) # 192.168.1.5
匹配手机号
r_phone = r'^(13|14|15|18)[0-9]{9}$' n=re.search(r_phone,'18910171654') print(n.group(0))
#18910171654
匹配邮箱
r_mail=r'^[a-zA-Z0-9]+@(\w+)\.(\w+)$' n=re.search(r_mail,'908099665@qq.com') print(n.group(0)) #908099665@qq.com
案例:实现简单计算器
计算(9-2*5 + 3*99*4*2998)/3+(5/2)
import re # cal_s = '1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )' multi_div_re = r'-{0,1}\d+\.?\d*[*/]-?\d+\.?\d*' # 乘除法正则 add_minus_re = r'-{0,1}\d+\.?\d*[+-]-?\d+\.?\d*' # 加减法正则 bracket_re = r'\([^()]+\)' # 括号正则 def remove_brack(express_str): express_str = re.sub('\s','',express_str) while True: s_brack = re.search(bracket_re, express_str.strip()) if s_brack: re_exp = s_brack.group() print('re_exp=', re_exp) # 得到去除括号的表达式 cal_res_mid = cal_exp(re_exp.strip('()')) # 乘除法 cal_res = cal_exp(cal_res_mid,add_minus_re) # 加减法 # 计算表达式 print('str=', cal_res) express_str = deal_symbol(express_str.replace(re_exp,str(cal_res))) print('express_str=', express_str) continue break res_mid = cal_exp(express_str) # 乘除法 res = cal_exp(res_mid, add_minus_re) # 加减法 return res def cal_exp(express_str,re_str = multi_div_re): """ 计算表达式 :param express_str: :param re_str: :return: """ while True: re_exp = re.search(re_str,express_str) if re_exp: res_exp = re_exp.group() # print(res_exp) res = cal_str(res_exp) express_str = deal_symbol(express_str.replace(res_exp,str(res))) print('cal_exp=',express_str) continue break return express_str def deal_symbol(express_str): """ 处理符号 :param express_str: :return: """ if '--' in express_str: # print('--') express_str = express_str.replace('--','-') elif '+-' in express_str: # print('+-') express_str = express_str.replace('+-','-') elif '++' in express_str: # print('++') express_str = express_str.replace('++','+') elif '-+' in express_str: # print('-+') express_str = express_str.replace('-+','-') # print('deal_symbol=',express_str) return express_str def cal_str(express_str): """ 计算2个元素的表达式的值 5+2 5*2 -5-2 5-2 5/2 :param express_str: :return: """ res = 0.0 # print(express_str) if '*' in express_str: x, y = express_str.split('*') res = round((float(x) * float(y)),2) elif '/' in express_str: x, y = express_str.split('/') res = round((float(x) / float(y)),2) elif '+' in express_str: x, y = express_str.split('+') res = round((float(x) + float(y)),2) elif '-' in express_str and express_str[0] == '-': # 处理-5-2 x, y,z = express_str.split('-') res = round((-float(y) - float(z)),2) elif '-' in express_str and express_str[0] !='-': # 处理5-2 x, y = express_str.split('-') res = round((float(x) - float(y)),2) return res if __name__ == '__main__': # cal_exp('1386418.32-1.2',add_minus_re) print(remove_brack(('(9-2*5 + 3*99*4*2998)/3+(5/2)')))