Python全栈之路6--正则表达式

正则本身就是一门语言:

  正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串,在文本处理方面功能非常强大,也经常用作爬虫,来爬取特定内容,Python本身不支持正则,但是通过导入re模块,Python也能用正则表达式,下面就来讲一下python正则表达式的用法。

下图列出了Python支持的正则表达式元字符和语法:

 一、Python正则的字符:

1、普通字符:

  大多数的字符和字母都会和自身匹配

2、元字符:

  元字符:.   ^   $   *   +   ?   { }   [ ]   |   ( )  \

2.1、[]详解

  例如,[abc] 将匹配"a", "b", 或 "c"中的任意一个字符;也可以用区间[a-c]来表示同一字符集,和前者效果一致。如果你只想匹配小写字母,那么 RE 应写成 [a-z].

2.2()详解

1
2
3
4
5
6
7
8
>>> p = re. compile ( '(a(b)c)d' )
>>> m = p.match( 'abcd' )
>>> m.group( 0 )
'abcd'
>>> m.group( 1 )
'abc'
>>> m.group( 2 )
'b'

2.3:+、*、?、{}详解

  贪婪模式和非贪婪模式:

  从前面的描述可以看到'*','+'和'?'都是贪婪的,但这也许并不是我们说要的,所以,可以在后面加个问号,将策略改为非贪婪,只匹配尽量少的RE。示例,

1
2
3
4
5
体会两者的区别:  <STRONG>findall 只匹配输出 分组内容如果是分组的话,如果不是分组的话都输出匹配到输出的内容< / STRONG>   后面介绍
>>> re.findall(r "a(\d+?)" , "a234b" ) # 非贪婪模式  如果\d+匹配的是两个数字的话,
         [ '2' ]
>>> re.findall(r "a(\d+)" , "a234b" )
         [ '234' ]<BR>

正则匹配的时候的 r 的作用

1
2
3
4
>>> re.findall(r "\bI" , "I love u" )           
[ 'I' ]
>>> re.findall(r "\bIl" , "Ilove u" )
[ 'Il' ]

二、re模块的各种方法

1、findall  (获取字符串中所有的匹配字符串)

findall(),可以将匹配到的结果以列表的形式返回,如果匹配不到则返回一个空列表,下面来看一下代码中的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import re
l = re.findall(r '\d' , '4g6gggg9,9' )             # \d代表数字,将匹配到的元素放到一个列表里
print (l)                                     # ['4', '6', '9', '9']
   
print (re.findall(r '\w' , 'ds.._ 4' ))           # ['d', 's', '_', '4'],匹配字母数字下划线
   
print (re.findall(r '^sk' , 'skggj,fd,7' ))       #  以sk开头的,['sk']
   
print (re.findall(r '^sk' , 'kggj,fd,7' ))        # []
   
print (re.findall(r 'k{3,5}' , 'ffkkkkk' ))       # 取前面一个字符‘k'的3到5次,['kkkkk']
   
print (re.findall(r 'a{2}' , 'aasdaaaaaf' ))      # 匹配前一个字符a两次,['aa', 'aa', 'aa']
   
print (re.findall(r 'a*x' , 'aaaaaax' ))          # ['aaaaaax'] 匹配前面一个字符0次或多次,贪婪匹配
   
print (re.findall(r '\d*' , 'www33333' ))        # ['', '', '', '33333', '']
   
print (re.findall(r 'a+c' , 'aaaacccc' ))         # ['aaaac'] 匹配前面一个字符的一次或多次,贪婪匹配
   
print (re.findall(r 'a?c' , 'aaaacccc' ))         # ['ac', 'c', 'c', 'c'] 匹配前面一个字符的0次或1次
   
print (re.findall(r 'a[.]d' , 'acdggg abd' ))     # .在[]里面失去了意义,所以结果为[]
   
print (re.findall(r '[a-z]' , 'h43.hb -gg' ))     # ['h', 'h', 'b', 'g', 'g']
   
print (re.findall(r '[^a-z]' , 'h43.hb -gg' ))    # 取反,['4', '3', '.', ' ', '-']
   
print (re.findall(r 'ax$' , 'dsadax' ))           # 以'ax'结尾 ['ax']
   
print (re.findall(r 'a(\d+)b' , 'a23666b' ))      # ['23666']
   
print (re.findall(r 'a(\d+?)b' , 'a23666b' ))     # ['23666']前后均有限定条件,则非贪婪模式失效
   
print (re.findall(r 'a(\d+)' , 'a23b' ))          # ['23']
   
print (re.findall(r 'a(\d+?)' , 'a23b' ))         # [2] 加上一个?变成非贪婪模式

find 的高级用法:?:

  默认是取分组()内的信息,但是我想让分组外的匹配信息也取到,就要用到 ?:

1
2
3
4
5
>>> import re
>>> re.findall(r "www.(baidu|laonanhai).com" , "sdfsd www.baidu.comwww.laonanhai.com" )
[ 'baidu' , 'laonanhai' ]
>>> re.findall(r "www.(?:baidu|laonanhai).com" , "sdfsd www.baidu.comwww.laonanhai.com" )
[ 'www.baidu.com' , 'www.laonanhai.com' ]

finditer():迭代查找

1
2
3
4
5
6
7
8
9
>>> p = re. compile (r '\d+' )
>>> iterator = p.finditer( '12 drumm44ers drumming, 11 ... 10 ...' )
>>> for match in iterator:
...  match.group() , match.span()
... 
( '12' , ( 0 , 2 ))
( '44' , ( 8 , 10 ))
( '11' , ( 24 , 26 ))
( '10' , ( 31 , 33 ))

2、match(pattern, string, flag=0)

  • 正则表达式
  • 要匹配的字符串
  • 标志位,用于控制正则表达式的匹配方式

Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
方法:
1.group ([group1, …]):
获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号 0 代表整个匹配的子串;不填写参数时,返回group( 0 );没有截获字符串的组返回 None ;截获了多次的组返回最后一次截获的子串。
2.groups ([default]):
以元组形式返回全部分组截获的字符串。相当于调用group( 1 , 2 ,…last)。default表示没有截获字符串的组以这个值替代,默认为 None
3.groupdict ([default]):
返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上。
4.start ([group]):
返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为 0
5.end ([group]):
返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引 + 1 )。group默认值为 0
6.span ([group]):
返回(start(group), end(group))。
7.expand (template):
将匹配到的分组代入template中然后返回。template中可以使用\ id 或\g< id >、\g<name>引用分组,但不能使用编号 0 。\ id 与\g< id >是等价的;但\ 10 将被认为是第 10 个分组,如果你想表达\ 1 之后是字符 '0' ,只能使用\g< 1 > 0

3、search(pattern, string, flag=0)

根据模型去字符串中匹配指定内容,匹配单个,只匹配一次,可以结合split 将匹配到内容分割 拼接 然后再次循环查找。因为findall尽管可以找到所有,但是在处理分组()时候分组外的内容匹配不到。而findall是返回列表 后面会有介绍

4、grouop和groups

group(0)  显示全部

group(1)  显示第一个分组()

group(2)  显示第二个分组()

如果没有分组或超出分组个数就会报错

5、sub(pattern, repl, string, count=0, flag=0)

用于替换匹配的字符串 pattern内必须为正则表达式,不能是正则表达式search或findall 查找到的赋值变量

  比如我的计算器处理括号的方法,用正则search匹配到后,不能直接将变量出入 sub的pattern,因为不起作用

sub 疑点 

sub(repl, string[, count]) | re.sub(pattern, repl, string[, count]): 

使用repl替换string中每一个匹配的子串后返回替换后的字符串。 
当repl是一个字符串时,可以使用\id或\g<id>、\g<name>引用分组,但不能使用编号0。 
当repl是一个方法时,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。 

subn方法  返回总共替换的次数

1
2
>>> re.subn(r '\d' , 'ZZ' , '23*,5sfds.6hsdf' )
( 'ZZZZ*,ZZsfds.ZZhsdf' , 4 )

subn(repl, string[, count]) |re.sub(pattern, repl, string[, count]): 

返回 (sub(repl, string[, count]), 替换次数)。 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import re 
p = re. compile (r '(\w+) (\w+)' )
s = 'i say, hello world!'
    
print p.subn(r '\2 \1' , s)
    
def func(m):
     return m.group( 1 ).title() + ' ' + m.group( 2 ).title()
    
print p.subn(func, s)
    
### output ###
# ('say i, world hello!', 2)
# ('I Say, Hello World!', 2)

 6、split(pattern, string, maxsplit=0, flags=0)

split(string[, maxsplit]) | re.split(pattern, string[, maxsplit]): 
按照能够匹配的子串将string分割后返回列表。maxsplit用于指定最大分割次数,不指定将全部分割。 

1
2
3
4
5
6
import re   
p = re. compile (r '\d+' )
print p.split( 'one1two2three3four4' )
    
### output ###
# ['one', 'two', 'three', 'four', '']

7  re.compile(strPattern[, flag]): compile 编译方法

如果一个匹配规则,以后要使用多次,就可以先将其编译,以后就不用每次都在去写匹配规则

  这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为
Pattern对象。 第二个参数flag是匹配模式,取值可以使用按位或运算符'|'
表示同时生效,比如re.I | re.M
  可以把正则表达式编译成一个正则表达式对象。可以把那些经常使用的正则
表达式编译成正则表达式对象,这样可以提高一定的效率。下面是一个正则表达式
对象的一个例子:

1
2
3
4
import re
text = "JGood is a handsome boy, he is cool, clever, and so on..."
regex = re. compile (r '\w*oo\w*' )
print regex.findall(text)   #查找所有包含'oo'的单词

三、原生字符串、编译、分组

1、原生字符串

细心的人会发现,我每一次在写匹配规则的话,都在前面加了一个r,为什么要这样写,下面从代码上来说明,

1
2
3
4
5
6
7
import re
#“\b”在ASCII 字符中代表退格键,\b”在正则表达式中代表“匹配一个单词边界”
print (re.findall( "\bblow" , "jason blow cat" ))    #这里\b代表退格键,所以没有匹配到
   
print (re.findall( "\\bblow" , "jason blow cat" ))   #用\转义后这里就匹配到了 ['blow']
   
print (re.findall(r "\bblow" , "jason blow cat" ))   #用原生字符串后就不需要转义了 ['blow']

你可能注意到我们在正则表达式里使用“\d”,没用原始字符串,也没出现什么问题。那是因为ASCII 里没有对应的特殊字符,所以正则表达式编译器能够知道你指的是一个十进制数字。但是我们写代码本着严谨简单的原理,最好是都写成原生字符串的格式。

2、编译

如果一个匹配规则,我们要使用多次,我们就可以先将其编译,以后就不用每次都在去写匹配规则,下面来看一下用法

1
2
3
import re
c = re. compile (r '\d' )                             #以后要在次使用的话,只需直接调用即可
print (c.findall( 'as3..56,' ))                    #['3', '5', '6']

3、分组

除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组,可以有多个组,分组的用法很多,这里只是简单的介绍一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import re
print (re.findall(r '(\d+)-([a-z])' , '34324-dfsdfs777-hhh' ))        # [('34324', 'd'), ('777', 'h')]
   
print (re.search(r '(\d+)-([a-z])' , '34324-dfsdfs777-hhh' ).group( 0 ))    # 34324-d 返回整体
print (re.search(r '(\d+)-([a-z])' , '34324-dfsdfs777-hhh' ).group( 1 ))    # 34324 获取第一个组
print (re.search(r '(\d+)-([a-z])' , '34324-dfsdfs777-hhh' ).group( 2 ))    # d 获取第二个组
print (re.search(r '(\d+)-([a-z])' , '34324-dfsdfs777-hhh' ).group( 3 ))    # IndexError: no such group
   
print (re.search(r "(jason)kk\1" , "xjasonkkjason" ).group())         #\1表示应用编号为1的组 jasonkkjason
   
print (re.search(r '(\d)gg\1' , '2j333gg3jjj8' ).group())                 # 3gg3 \1表示使用第一个组\d
   
# 下面的返回None 为什么是空?而匹配不到3gg7,因为\1的不仅表示第一组,而且匹配到的内容也要和第一组匹配到的内容相同,第一组匹配到3,第二组匹配到7 不相同所以返回空
print (re.search(r '(\d)gg\1' , '2j333gg7jjj8' ))
   
print (re.search(r '(?P<first>\d)abc(?P=first)' , '1abc1' ))              # 1abc1 声明一个组名,使用祖名引用一个组 
   
r = re.match( '(?P<n1>h)(?P<n2>\w+)' , 'hello,hi,help' )   # 组名的另外一种用法
print (r.group())                                # hello 返回匹配到的值
print (r.groups())                               # ('h', 'ello')返回匹配到的分组
print (r.groupdict())                            # {'n2': 'ello', 'n1': 'h'} 返回分组的结果,并且和相应的组名组成一个字典
   
# 分组是从已经匹配到的里面去取值
origin = "hello alex,acd,alex"
print (re.findall(r '(a)(\w+)(x)' ,origin))                  # [('a', 'le', 'x'), ('a', 'le', 'x')]
print (re.findall(r 'a\w+' ,origin))                         # ['alex', 'acd', 'alex']
print (re.findall(r 'a(\w+)' ,origin))                       # ['lex', 'cd', 'lex']
print (re.findall(r '(a\w+)' ,origin))                       # ['alex', 'acd', 'alex']
print (re.findall(r '(a)(\w+(e))(x)' ,origin))               # [('a', 'le', 'e', 'x'), ('a', 'le', 'e', 'x')]
   
r = re.finditer(r '(a)(\w+(e))(?P<name>x)' ,origin)
for i in r :
     print (i,i.group(),i.groupdict())
'''
     [('a', 'le', 'e', 'x'), ('a', 'le', 'e', 'x')]
     <_sre.SRE_Match object; span=(6, 10), match='alex'> alex {'name': 'x'}
     <_sre.SRE_Match object; span=(15, 19), match='alex'> alex {'name': 'x'}
'''
   
print (re.findall( '(\w)*' , 'alex' ))                   # 匹配到了alex、但是4次只取最后一次即 x 真实括号只有1个
print (re.findall(r '(\w)(\w)(\w)(\w)' , 'alex' ))       # [('a', 'l', 'e', 'x')]  括号出现了4次,所以4个值都取到了
   
origin = 'hello alex sss hhh kkk'
print (re.split(r 'a(\w+)' ,origin))                   # ['hello ', 'lex', ' sss hhh kkk']
print (re.split(r 'a\w+' ,origin))                     # ['hello ', ' sss hhh kkk']

 

转载于:https://www.cnblogs.com/kenfang/articles/5828889.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值