python正则表达式re各常用函数详解

        本章介绍python文本提取之re正则表达式,主要函数和知识点有:re.findall(),re.sub(),re.match(),re.search(),各种正则符号,打组,贪婪与非贪婪等。

目录

1.  re.findall(pattern, string, flags=0)

(1)提取任意字符[]

(2)提取数字/非数字

(3)提取中文、下划线、数字、英文

(4)提取空白字符

(5)限定字符数量

(6)连续匹配

(7)边界匹配

(8)忽略大小写

扫描二维码关注公众号,回复: 15947011 查看本文章

(9)匹配任意字符

(10)打组

(11)贪婪与非贪婪

2.  re.sub(pattern, repl, string, count=0, flags=0)

3.  re.match(pattern, string, flags=0)

4.  re.search(pattern, string, flags=0)


        为了测试正则表达式,首先生成一段假信息用于测试,不懂可以看我的faker篇。

import faker

f = faker.Faker('zh_CN')


def data_line():
    name = f.name()
    phone = f.phone_number()
    job = f.job()
    address_yb = f.address()
    address = address_yb.split(' ')[0]
    bm = address_yb.split(' ')[1]
    data = f'姓名:{name} 电话:{phone} 工作:{job} 地址:{address} 邮政编码:{bm} '
    return data


data = ''
for i in range(5):
    data = data + data_line()

print(data)
图1 用fake生成的假信息

        关于正则表达式需要记住的一些符号,其实也不必特意去记,反而容易忘。多用,自然就记得了。为了方便,我把意义相反或者相近的写一块,用中文“,”隔开。

符号1,符号2 符号1解释。符号2解释
[],[^]
字符种类,提取出现在[]中的任意字符。后者相反,都不提取
[0-9a-zA-Z],[^0-9a-zA-Z]
所有0-9a-zA-Z元素。后者相反
\d,\D
\d所有的数字(相当于[0-9])。
\D所有的非数字(相当于[^0-9])
\w,\W
\w中文下划线数字英文(相当于[0-9a-zA-Z_])。\W与\w相反,特殊字符,如 $、&、空格、\n、\t等
\s,\S
\s所有空白的字符(如 \t\n\r\f\v,相当于[ \t\n\r\f\v])。\S相反,所有非空白字符
{min,max}、{num}
{min,max}只提取符合条件的连续min-max个元素。{num}只提取符合条件的连续num个元素
[]+,\d+
都为连续匹配,前者为连续匹配[]内任意字符,后者连续匹配所有数字
'excel*','excel+','excel?'
*前面的一个字符l匹配0次或者无限次。+前面的一个字符l匹配1次或者无限次。?前面的一个字符l匹配0次或者1次
^,$
指定从开头开始符合条件的数。指定结尾是符合条件的数(与第一个的区别是它只写在''的最前面或者最后面,而第一个是写在[]里的)
re.I
忽略大小写 IGNORECASE
. 
.{num}
.*
.+
.取一个元素(全部取完 每个元素分开)。
.{num}取前num位(一个)。
后二皆整体取全
(?) ()打组,组内的?表示非贪婪模式

        千言万语说不清,我们用re.findall()函数来实操一下。

1.  re.findall(pattern, string, flags=0)

      查找全部符合pattern条件的元素。

        pattern:条件表达式

        string:被提取的字符串

        flags:控制正则表达式的匹配方式,如是否区分大小写,多行匹配等

(1)提取任意字符[]

import re
a = re.findall('[0-9]',data)
b = re.findall('[0-9]+',data)
print('输出a:\n',a)
print('输出b:\n',b)
图2 [0-9]与[0-9]+

        这里先用re.findall()函数学习上面表格的内容,先学会条件表达式,再跟着学其他函数。(提醒鼠标滑得快的朋友,data在上面有定义过)

        []表示匹配[]里任意字符,这里[0-9]表示匹配0-9(包含0和9)中任意一个字符,说任意一个,即把匹配到的字符每一个作为列表的一个元素,如输出a。当在[]外面加上+后,表示连续匹配,遇到连续的0-9数字,它会作为一个整体去输出,如输出b。

        不止0-9,如果想提取其他,也可以直接写在[]中,如想提取0-9、A-Z、a-z和省字:

a = re.findall('[0-9A-Za-z省]',data)
print(a)
图3 提取0-9、A-Z、a-z和省字

        这样就把含有0-9、A-Z、a-z和省字的都一一提取出来了,如果想要连续的,同理,在[]后加上+即可。

        如果想要一一提取除0-9、A-Z、a-z和省字以外的元素,可在[]里面的开头加上^,如:

a = re.findall('[^0-9A-Za-z省]',data)
print(a)
图4 任意提取

        想要连续匹配也同理,在后面加上+,如图2的b。

(2)提取数字/非数字

        \d提取数字,\D提取非数字。

import re
a = re.findall('\d',data)
b = re.findall('\d+',data)
c = re.findall('\D+',data)
print('输出a:\n',a)
print('输出b:\n',b)
print('输出c:\n',c)
图5 \d与\D

         \d为提取数字,如a。\D为非数字,如b。+表示连续匹配,有个印象,详细的连续匹配后面再讲。

(3)提取中文、下划线、数字、英文

        \w提取中文、下划线、数字、英文。大写字母\W相反。

por = '十多年了,I still love you li_wei.$#% *&'
a = re.findall('\w+',por)
b = re.findall('\W+',por)
print(a)
print(b)
图6 \w与\W

(4)提取空白字符

        \s提取所有空白字符,包括 \t、\n、\r、\f、\v和空格。大写字母\S则相反。

por = '十多年了,I still love you li_wei.$#% *& \n \r \t'
a = re.findall('\s+',por)
b = re.findall('\S+',por)
print(a)
print(b)
图7 空白字符

        所谓的空白字符就是输出的时候表现为空白的字符,如 \t、\n、\r、\f、\v和空格。以下打印一下por,看看它是否“空白”:

por = '十多年了,I still love you li_wei.$#% *& \n \r \t'
print(por)
图8 输出含有空白字符的语句

        如图,\t、\n、\r、\f、\v和空格 都表现为空白,\s就是专门提取这些空白字符的。

(5)限定字符数量

        {min,max}为表示前者表达式中提取符合条件的数的范围。{num}特定数量。

por = '湖&南省&潮州县&静安韦街&座'
a = re.findall('\w{2,3}',por)
print(a)
图9 限定数量

         \w为提取中文下划线数字英文,后面指定了2-3位。在por中,“湖”字后面是“&”,由于“&”不符合中文下划线数字英文,所以“湖”符合条件,但是只有一个字,不符合{2,3},所以没有得到提取。后面“潮州县”符合中文下划线数字英文,也符合2-3位,所以提取。再后面“静安韦街”符合,但是超出了2-3位的条件,所以只取最多的3位,得到“静安韦”。

(6)连续匹配

        +表示前面的条件匹配1次或无数次。*表示前面的条件匹配0次或无数次。?表示前面的条件匹配0次或1次。(看到例子就懂了)

por = 'wor word wordd worddd'
a = re.findall('word+',por)
b = re.findall('word*',por)
c = re.findall('word?',por)
print(a)
print(b)
print(c)
图10 连续匹配

        +表示前面的条件匹配1次或无数次。如a,+号前面的字符d匹配1次或者无限次,就是说,可以是 word , wordd , worddd , wordddd ...等,符合这些的,都能被提取出来。

        *表示前面的条件匹配0次或无数次。如b,*号前面的字符d匹配0次或者无限次,0次就是就算没有d但是有 wor 也能提取出来,所以可以是 wor , word , wordd , worddd , wordddd ...等。

        ?表示前面的条件匹配0次或1次。如c,?号前面的字符d匹配0次或者1次,也就是说可以是 wor , word .只有这两个,por中出现这两个都能提取出来,而多余的d则不会提取。

(7)边界匹配

        ^表示指定开头,$表示指定结尾(难说清楚,直接看案例,随便写的号码哈)

edge_1 = '16915158890abcdef'
edge_2 = 'abcdef16915158890'

a = re.findall('^\d{11}',edge_1)
b = re.findall('^\d{11}',edge_2)
print(a)
print(b)
图11 边界匹配_开头

         如图11,^\d{11}表示从头开始,匹配11位的连续数字。在a中,用^指定了开头,由于edge_1的开头符合\d即数字,且符合11位数,所以能提取。在b中,由于edge_2的开头为a,不符合\d,所以匹配不成功,放回空列表。

        $也同理,指定结尾。

edge_1 = '16915158890abcdef'
edge_2 = 'abcdef16915158890'

a = re.findall('\d{11}$',edge_1)
b = re.findall('\d{11}$',edge_2)
print(a)
print(b)
图11 边界匹配_结尾

         如图,对于a,用$指定了结尾,由于结尾不符合\d{11},即不符合以11位数字结尾,所以a提取不出来。对于b,符合以11位数字结尾,所以能提取到。

        请记住,对于指定开头,用^;对于指定结尾,用$。而不是用$去指定开头。注意区分第(1)点里的^,^写在[]里的最前面,表示[]里取反;而^写在整体的最前面,表示边界匹配。

(8)忽略大小写

        flags = re.I 忽略大小写,该参数写在函数体里。

fi = 'excel Excel excEL word EXCEL WORD'
a = re.findall('excel',fi,flags=re.I)
print(a)
图12 忽略大小写

(9)匹配任意字符

        在正则表达式中,. 表示除回车符 \n 以外的任何一个字符,起到通配符的作用。

fi = '今天我吃了鱼呀,\n今天我吃了鸡肉呀,\n今天我吃了鸡扒呀,\n今天我什么都没有吃'
a = re.findall('今天我吃了.*呀',fi)
print(a)
图13 通配符

         . 在此处代表了除回车 \n 以外的任意字符,加上连续匹配符*号, .* 在一起表示为0个或者无数个字符。在该案例下,前面有“今天我吃了”,后面有“呀”,表示处于这两个之间可以存在0个或者无数个字符,就是说符合 “今天我吃了......呀” 的句子都可以被提取出来。

        在这可能有疑问,如何只提取中间......里的部分呢?这就需要给句子打个组,看10。

(10)打组

        在正则表达式中,()表示打组,只输出()里的内容。

fi = '今天我吃了鱼呀,\n今天我吃了鸡肉呀,\n今天我吃了鸡扒呀,\n今天我什么都没有吃'
a = re.findall('今天我吃了(.*)呀',fi)
print(a)
图14 打组

         直接在 .* 里加个()即可。(需要注意的是本案例的原句子是含有回车符 \n 的,假如没有回车符,打组会出现另外一个问题

        假如原句子没有回车,我们看看打组会出现什么问题:

图15 打组问题

         在这,只提取到一个!框中的内容都全列在一起了,而不是我们想要得到的(我们想要得到图14那样,只含食物的)。这是因为 .* 不能匹配 \n,所以如图14那样存在 \n的句子,它在匹配时自动在 \n就暂停寻找,得到其中一个结果,然后在 \n后面重新找第二个,这样我们达到了预期。但是像图15那样没有 \n的句子,它除了 \n都匹配,所以它就一直匹配到底,导致粘在一起了。此类问题广泛出现在爬虫中,所以需要掌握非贪婪模式。

(11)贪婪与非贪婪

        像图15,它就属于贪婪了。我们只需要在条件后面加上?号,即可启动非贪婪模式。

fi = '今天我吃了鱼呀,今天我吃了鸡肉呀,今天我吃了鸡扒呀,今天我什么都没有吃'
a = re.findall('今天我吃了(.*?)呀',fi)
print(a)
图16 非贪婪模式

        启动非贪婪模式后,它后面匹配到第一个“呀”就会暂停,得到了其中一个结果。接着再继续寻找下一个,知道检索完毕为止。这样就解决了图15的打组出现的问题。

        注意,当?用在()组里时,代表启动非贪婪模式;当?用在非组里,就是连续匹配(0次或1次)的意思。

        基本条件表达式pattern学习完毕,别开心,到这还没学完,re.findall(pattern, string, flags=0)里的pattern,还有re.sub(),re.match(),re.search()等函数,不过这些函数里面的参数我们在上面都学过了,所以学re.sub(),re.match(),re.search()很快,基本一看就过,下面我们继续。

2.  re.sub(pattern, repl, string, count=0, flags=0)

      正则替换,支持调用自定义函数。

        pattern:被替换的字符串

        repl:替换成什么str,支持调用自定义函数

        string:字符串,句子

        count:替换的次数,默认0为无数次

        flags:控制正则表达式的匹配方式,如是否区分大小写,多行匹配等

import re
st = 'word word word excel ppt office Word WORD '
a = re.sub('word','1111',st)
b = re.sub('word','1111',st,count=1)
c = re.sub('word','1111',st,flags=re.I)
print('替换全部word:')
print(a)
print('\n替换一次word:')
print(b)
print('\n替换全部word,忽略大小写:')
print(c)
图17 正则替换 re.sub()

         把word替换成1111,图中 a,b,c 分别列出了 替换全部、只替换一次 和 替换全部且忽略大小写 的情况。

        正则表达式的替换优势于replace的原因是正则表达式可以调用自定义函数,此替换是高级替换,而replace不行。高级替换举例(.group()为组内元素的提取,后面会说到):

ss = '孙杨 98 李红 65 郭靖 85 扬程 43 彭凯文 70'

def rep_judge(x):
    score_str = x.group()
    score = eval(score_str)
    if score >= 90:
        return '优秀'
    elif score >= 80:
        return '良好'
    elif score >= 60:
        return '及格'
    else:
        return '不及格'

a = re.sub('\d+',rep_judge,ss)
print(a)
图18 正则高级替换

         eval()函数为把字符串包含的数字(字符串类型)转为数字类型。高级替换还有很高级的功能,这里就不一一列举了。

3.  re.match(pattern, string, flags=0)

      从头匹配一个符合pattern的元素,匹配成功则返回一个对象,匹配不成功则返回None(开头不符合,直接None)

        pattern:条件表达式

        string:被提取的字符串

        flags:控制正则表达式的匹配方式,如是否区分大小写,多行匹配等

ss_1 = 'A83C72D1D8E67'
ss_2 = '83C72D1D8E67'
a = re.match('\d',ss_1)
b = re.match('\d',ss_2)
print(a)
print(b)
图19 re.match

         a在ss_1的开头中匹配 \d,即一个数字,由于开头不是数字,所以直接匹配不成功,返回None;b在ss_2的开头中匹配 \d,匹配成功返回一个对象。如果开头不符合条件,直接返回None了,后面的都不关事,所以 re.match() 用于检验开头!!!

print(a.group())
图20

         b匹配成功,可以用.group()在对象中提取元素,如图20。其实 .group() 括号中也可以输入数字,表示提取第几个组的内容。由于re.match()只匹配一个,直接.group() 即可提取出来,所以在这里不研究group的参数,在 re.search()进一步讨论。

4.  re.search(pattern, string, flags=0)

      从头匹配符合pattern的元素,匹配成功则返回一个包含所有符合元素的对象,没有匹配到则返回None(开头不符合不要紧,接着匹配,把所有符合的都找到,打包在组中;都找不到就返回None)

        参数与 re.match一样,就不重复阐述了。

ss = '123abc¥456'
a = re.search('([0-9]*)([a-z]*)(\W)([0-9]*)',ss)
print(a.groups())                     # 全部 分组提取
print(a.group())                      # 全部 一起提取(括号内为空或者0)
print(a.group(1))
print(a.group(2))
print(a.group(3))
print(a.group(4))
图21 re.search

        把数字、a-z、\W、数字 划分打组,然后一一提取。与图14不同的是,这里返回的是一个对象,而图14返回的是一个列表,返回对象需要用.group()提取组里的内容。

        group()里的数字具体可参考图21,.groups()为提取全部,返回一个元组;.group(0)或.group()为提取全部,但是会合并在一起;.group(1)为提取第一个组,以此类推。

        需要注意的是,如果传入.group(5),超出了组数的范围,则会报错 “ no such group ” 。

猜你喜欢

转载自blog.csdn.net/m0_71559726/article/details/130178250