正则表达式
正则表达式是一个特殊的字符序列,可以检测一个字符串是否与我们设定的字符序列相匹配。
一、findall()方法
re模块下的findall()方法可以查找对应字符/字符串
最简单的方法可以查找字符串中的特定字符串:
importre
a= 'C|C++|Java|C#|Python|Javascript'r= re.findall('Python', a) #返回一个包含结果的列表
print(r)iflen(r):print('字符串中包含Python。')else:print('字符串中不包含Python。')
二、元字符和普通字符
对于上例findall()方法中的第一个实参'Python'即为普通字符,即表示字符本意的字符。
元字符:包括\d匹配0-9中的数字;\D匹配一个非数字字符,即[^0-9];等等。
importre
a= 'C0C++7Java8C#9Python6Javascrip'
#通过'\d'匹配数字字符
r = re.findall('\d',a)print(r)#通过'\D'匹配数字字符
r = re.findall('\D',a)print(r)
三、字符集
正则表达式中通过字符集设定字符的可能值,字符集通过[]表示。
如[0-9]表示0到9中的一个数字,即等同于元字符\d;[cf]字符集匹配c或者f;a[hc]b匹配一个三个字符的字符串,第一个字符为a,第二个字符为h或c,第三个字符为b;[^1]匹配所有不是1的字符。
importre
s0= 'He1ll2o,Wor3ld.'s1= 'abc, acc1, adc, aec, a1efc, ahc'
#匹配为e或o的字符
r0 = re.findall('[eo]', s0)#匹配0-3之间的字符
r00 = re.findall('[0-3]', s0)#匹配非小写字母的字符,且第二个字符为逗号
r01 = re.findall('[^a-z],',s1)#匹配第一个字符为a,第三个字符为c,第二个字符为c或f的字符串
r1 = re.findall('a[cf]c', s1)print(r0)print(r00)print(r01)print(r1)
四、概括字符集
上述提及的\d等元字符可以理解为概况字符集,即概况描述某一类字符的集合,如\d也可以表示为[0-9]字符集。
元字符\D可以用字符集[^0-9]表示;
元字符\w表示匹配单词字符(字母,数字,下划线)可以用字符集[A-Za-z0-9_]表示;
元字符\W表示匹配非单词字符,即%¥#@此类,也包括空格,制表符,回车,换行等空白字符;
元字符\s表示匹配空白字符,即空格、回车、换行符等。
importre
a= '_&python# 2java8\n9@php'
#\d匹配一个数字字符
r = re.findall('\d', a)#\D匹配一个非数字字符
r1 = re.findall('\D', a)#\w匹配一个单词字符(数字或字母字符或下划线)
r2 = re.findall('\w', a)#\W匹配一个非单词字符
r3 = re.findall('\W', a)#\s匹配一个空白字符
r4 = re.findall('\s', a)print(r)print(r1)print(r2)print(r3)print(r4)
五、数量词
对于以下代码:
importre
a= 'python 1111java678php'r= re.findall('[a-zA-Z]', a)print(r)#输出:['p', 'y', 't', 'h', 'o', 'n', 'j', 'a', 'v', 'a', 'p', 'h', 'p']
因为参数正则表达式为一个字符集,仅匹配单个字符;如果需要以单词的形式作为输出的元素,则需要用到数量词这个方式。
importre
a= 'python 1111java678php'r= re.findall('[a-z]{3,6}', a)print(r)#输出:['python', 'java', 'php']
参数中的[a-z]表示字符的匹配条件,{3,6}表示需要匹配的数量为3-6个,需要注意这里Python会默认采用贪婪的匹配方法,即如果已经匹配到3个字符后还会继续匹配,直到不再满足匹配条件,或达到匹配数量的最大值为止。
importre
a= 'python 1111java678php'r= re.findall('[a-z]{3,6}', a)print(r)#输出:['python', 'java', 'php']
Python默认为贪婪匹配,如果要采用非贪婪模式匹配,则需要在数量词后加个'?':
importre
a= 'python 1111java678php'
#非贪婪匹配方式
r = re.findall('[a-z]{3,6}?', a)print(r)#输出:['pyt', 'hon', 'jav', 'php']
*除了上述的'{}'表示数量词之外,也可以通过‘*’表示匹配星号前面字符0次或1次或无限次,通过'+'表示匹配加号前面字符至少1次,通过'?'表示匹配问号前面字符0次或1次。。
importre
a= 'pytho0python1pythonn2'
#匹配*前面的n0次或1次或无限次
r = re.findall('python*', a)#匹配+前面的n至少一次
r1 = re.findall('python+', a)#匹配?前面的n0次或1次
r2 = re.findall('python?', a)print(r)print(r1)print(r2)#输出:
'''['pytho', 'python', 'pythonn']
['python', 'pythonn']
['pytho', 'python', 'python']'''
需要注意这里问号和前述数量词{}之后问号的区别。
通过数量词实现上例,注意对比:
importre
a= 'pytho0python1pythonn2'
#通过数量词{}取n1-2次,贪婪模式
r = re.findall('python{1,2}', a)#通过数量词{}取n1-2次,非贪婪模式
r1 = re.findall('python{1,2}?', a)print(r)print(r1)#输出:
'''['python', 'pythonn']
['python', 'python']'''
六、边界匹配
如果有一个数字字符串,需要判断是否为一个QQ号码,比如qq='10000001'。
QQ号码的判断规则(假设)为4-8位的数字,如果单纯通过数量词查找:r = re.findall('\d{4,8}', qq),对于本例也是可以查找匹配出的,但如果
qq = '1234567890',通过前面的正则表达式'\d{4,8}'也可以匹配到数字字符串,但显然这个QQ号码超出了8位,这里就需要通过边界匹配符来界定匹配范围:
importre
qq= '1234567890'
#QQ号为4-8位,检测是否为QQ号码#通过边界匹配符'^...$'匹配整个字符串#第一个^表示从整个字符串开头匹配4-8个数字#最后一个$表示从整个字符串最末尾往前匹配4-8个数字#两者都匹配到的才为最终匹配到的结果
r = re.findall('^\d{4,8}$', qq)print(r)
再单独理解一下边界匹配符的第一个^和最后一个$的作用:
importre
qq= '123a4567890abchf'
#通过'^'匹配整个字符串开头的3-4个数字
r0 = re.findall('^\d{3,4}', qq)#通过'$'匹配整个字符串末尾的3-4个字母
r1 = re.findall('[a-zA-Z]{3,4}$', qq)print(r0)print(r1)'''输出:
['123']
['bchf']'''
七、组
前面数量词的使用中数量词针对的是其前面的单个字符,也可以通过组的概念将整组字符适用到数量词上:
importre
a= 'PythonPythonPythonPythonJSJSPython'
#找出是否含有连续三个Python,如果有则返回的是单个组,否则返回空列表
r = re.findall('(Python){3}', a)#找出是否含有连续三个PYthon且连着两个JS
r0 = re.findall('(Python){3}(JS){2}', a)#找出是否含有连续三个PYthon且连着三个JS
r1 = re.findall('(Python){3}(JS){3}', a)print(r)print(r0)print(r1)'''输出:
['Python']
[('Python', 'JS')]
[]'''
八、匹配模式参数
findall()还有第三个参数flags即指匹配模式,当参数值设为re.I则忽略正则表达式中的字母大小写;当参数值设为re.S则忽略
importre
language= 'PythonC#\nJavaPHP'
#忽略大小写的模式
r = re.findall('c#', language, re.I)#‘.’本来表示除了换行之外的所有字符,re.S模式表示'.'可以匹配任意字符#匹配整个字符串的最后两个字符
r0 = re.findall('.{2}$', language, re.S)#匹配c#或C#及后面1-3个字符(非贪婪)
r1 = re.findall('c#.{1,3}?', language, re.I |re.S)#匹配c#或C#及后面1-3个字符(贪婪)
r1 = re.findall('c#.{1,3}', language, re.I |re.S)print(r)print(r0)print(r1)'''输出:
['C#']
['HP']
['C#\nJa']'''
九、sub()/match()/search()函数
1、sub()函数可以实现查找后替换的功能。参数格式依此为正则表达式、需要匹配后替换的字符串(或函数)、原始字符串、匹配次数、模式参数。
importre
language= 'PythonC#JavaC#PHPC#'r= re.sub('C#', 'Javascript', language)print(r)#输出:PythonJavascriptJavaJavascriptPHPJavascript
第四个参数表示匹配后替换的次数,默认为0,表示不限制次数的替换,如果设置为非0的某个数值,则表示替换的最大次数。
importre
language= 'PythonC#JavaC#PHPC#'
#无限次替换
r1 = re.sub('C#', 'Javascript', language, 0)#最多1次替换
r2 = re.sub('C#', 'Javascript', language, 1)#最多2次替换
r3 = re.sub('C#', 'Javascript', language, 2)#对比字符串内置函数replace()
language0 = language.replace('C#', 'Javascript')print(r1)print(r2)print(r3)print(language0)'''输出:
PythonJavascriptJavaJavascriptPHPJavascript
PythonJavascriptJavaC#PHPC#
PythonJavascriptJavaJavascriptPHPC#
PythonJavascriptJavaJavascriptPHPJavascript'''
第二个参数除了直接指定为字符串,也可以指定为某个函数,其功能是把正则匹配出的字符串作为该函数参数,返回的字符串作为替换的字符串。
这里需要注意正则参数传递的时候实际是一个正则对象,需要通过访问其group()获得真正匹配到的字符串。
importredefconvert(value):
v=value.group()return v+'!'language= 'PythonC#JavaC#PHPC#'r= re.sub('C#', convert, language)print(r)#输出:PythonC#!JavaC#!PHPC#!
对于上例的convert()函数中,通过value.group()获取了匹配到的实际的字符串,这里value即是一个正则对象,除了group()函数,它还有span()方法,可以获取匹配到的字符串在原始字符串中的索引:
importredefconvert(value):#打印匹配到的字符在原始字符串中的索引
print(value.span())
v=value.group()return v + '!'language= 'PythonC#JavaC#PHPC#'r= re.sub('C#', convert, language)print(r)'''输出:
(6, 8) #匹配到的第一个C#对应索引为6,7
(12, 14)#匹配到的第一个C#对应索引为12,13
(17, 19)#匹配到的第一个C#对应索引为17,18
PythonC#!JavaC#!PHPC#!'''
importre
s= 'A8C3721D86'
#大于等于6的数字替换成9,其余数字替换为0
defconvert(value):
n=int(value.group())if n >= 6:return '9'
else:return '0'r= re.sub('\d', convert, s)print(r)#输出:A9C0900D99
importre
s= 'A8C372R1X16D86F333P50F7N'
#剔除所有的单个数字字符#两位数字大于50的替换为100,其余替换为0
defconvert(value):
n=int(value.group())if n < 10:return ''
elif n < 50:return '0'
else:return '100'r= re.sub('\d{1,10}', convert, s)print(r)#输出:AC100RX0D100F100P100FN
2、search()/match()函数
(1)match()尝试从字符串开头去匹配,匹配成功则返回第一个匹配的字符串,返回一个Match对象;如果没有匹配到则返回None。
(2)search()将搜索整个字符串,直到找到第一个匹配的字符串,返回一个Match对象;如果没有匹配到则返回None。
importre
s= 'A8C3721D86'r= re.match('\d', s)print(r)
r0= re.search('\d', s)#通过对象的group()获取匹配到的字符串
print(r0.group())
需要特别注意的是:search()和match()函数只最多匹配一次,匹配到一次后不再继续查找匹配,而findall()函数会查找整个字符串范围。
3、分组
除了前述提到的数量重复的作用外,还可以通过分组括号指定需要匹配的字符:
importre
s= 'life is short,i use python,\n i love python'r= re.search('life(.*)python', s, re.S)#group()获取整个分组的匹配,即'life(.*)python'
print(r.group())#也可以加下标0获取上述字符串
print(r.group(0))#可以加下标1获取上述字符串中的小分组
print(r.group(1))#groups()获取大分组下的小分组字符串,即上述(.*)
print(r.groups())#通过findall()是最方便直观的:
r1 = re.findall('life(.*)python', s, re.S)print(r1)print()
importre
s= 'life is short,i use python, i love python'
#多个分组的情况
r = re.search('life(.*)python(.*)python', s, re.S)#group()获取整个分组的匹配,即'life(.*)python(.*)python'
print(r.group(0))#可以加下标1获取上述字符串中的第一个小分组
print(r.group(1))#可以加下标2获取上述字符串中的第二个小分组
print(r.group(2))#通过groups()获取小分组
print(r.groups())#通过findall()是最方便直观的:
r1 = re.findall('life(.*)python(.*)python', s, re.S)print(r1)