人生苦短,我用Python05:正则表达式

时间:July 12, 2017 分类:

目录:

正则表达式

正则表达式在大多数语言中都存在,对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

不只是python有正则表达式,其他的语言也有,都是通过语言来调用正则表达式,而不是语言内置。

正则表达式分两种匹配方式,普通字符和元字符。

正则表达式使用方式

import re
re.findall(匹配规则,匹配字符串,标识符)

返回列表,将匹配到的字符

普通匹配字符串

>>> re.findall('why','wangwhy')
['why']

元字符

元字符 含义
. 匹配任意一个字符
^ 代表开头
$ 代表结尾
* 代表零个或者多个前一个字符
+ 代表一个或者多个前一个字符
? 代表零个或者一个前一个字符
{} 代表括号内数量的前一个字符
竖杠(怎么转义都不行,就是不显示) 代表或的关系
[] 括号内元素
() 分组
\ 转义,其后的元字符去除特殊功能,其后的普通字符实现特殊功能,引用对应自组所匹配的字符串

元字符实例

元字符'.'匹配

'.'可以匹配任意一个字符

>>> re.findall('w.y','wangwhy')
['why']
>>> re.findall('wh.','wangwhy')
['why']

如果改为'.*'代表匹配任意零到多个,不过正则匹配默认是贪婪的

>>> re.findall('w.*','wangwhy')
['wangwhy']

元字符'^'和'$'

匹配以why结尾的字符串

>>> re.findall('why$','wangwhy')
['why']

匹配以why开头的字符串

>>> re.findall('^why','wangwhy')
[]

匹配以wang开头的字符串

>>> re.findall('^wang','wangwhy')
['wang']

元字符*+?{}

>>> re.findall('why','wangwhyyyyyyyyyy')
['why']
>>> re.findall('why+','wangwhyyyyyyyyyy')
['whyyyyyyyyyy']
>>> re.findall('why?','wangwhyyyyyyyyyy')
['why']
>>> re.findall('why*','wangwhyyyyyyyyyy')
['whyyyyyyyyyy']
>>> re.findall('why{3}','wangwhyyyyyyyyyy')
['whyyy']
>>> re.findall('why{3,5}','wangwhyyyyyyyyyy')
['whyyyyy']

元字符|

>>> re.findall('why|wxy','wangwhywxywmy')
['why', 'wxy']

下面的方式是不行的

>>> re.findall('wh|xy','wangwhywxywmy')
['wh', 'xy']

元字符[]

首先在这里'.'不具备元字符的能力

>>> re.findall('w[.]y','wangwhy')
[]
>>> re.findall('w[.]y','wangw.y')
['w.y']

代表或的关系,下边为匹配h或x

>>> re.findall('w[hx]y','wangwhywxy')
['why', 'wxy']

代表a到z的所有字符,这个a到z是根据ascii码的顺序

>>> re.findall('[a-z]','why123')
['w', 'h', 'y']

代表0-9的所有字符

>>> re.findall('[0-9]','why123')
['1', '2', '3']

'^'在这里代表非的意思,就是除0到9之外的任意字符

>>> re.findall('[^0-9]','why123')
['w', 'h', 'y']

元字符\

'\'代表转移,把特殊的元字符变为正常的字符,把正常的字符富裕特殊的含义

特殊字符变为正常字符,我们要获取(why),在不转义的情况下

>>> re.findall('(why)','wang(why)')
['why']

转义后为

>>> re.findall('\(why\)','wang(why)')
['(why)']

正常字符富裕特殊含义,一下列举了常用的字符

  • '\b'单词边界
  • '\d'匹配数字字符
  • '\D'匹配非数字字符
  • '\s'匹配任何空白字符,等价于[ \t\n\r\f\v]
  • '\S'匹配任何非空白字符
  • '\w'匹配任何字母数字字符,等价于[a-zA-Z0-9_]
  • '\W'匹配任何非字母数字字符,等价于[^a-zA-Z0-9_]
>>> re.findall('\d+','why123')
['123']

单词边界示例

>>> re.findall(r'\b\w+\b','why wanghongyu')
['why', 'wanghongyu']

这里我们在匹配的方式前面添加了r,这是为什么呢?下边是不加r的匹配到的

>>> re.findall('\b\w+\b','why wanghongyu')
[]

原因是python语言来调用正则表达式,但是由于'\b'在python中有别的含义,传递到正则表达式的参数不是正则表达式要的'\b',而是python语言中'\b'的含义——ascii码中的退格,所以没有匹配到,如果这时候我们把'\b'转义一下就可以了,即'\b',这样正则表达式接收到参数就为'\b'了。

>>> re.findall('\\b\w+\\b','why wanghongyu')
['why', 'wanghongyu']

ASCII码值(十进制)特殊字符的转义序列符

  • \a 响铃(BEL) 007
  • \b 退格(BS) 008
  • \f 换页(FF) 012
  • \n 换行(LF) 010
  • \r 回车(CR) 013
  • \t 水平制表(HT) 009
  • \v 垂直制表(VT) 011
  • \ 反斜杠 092
  • ? 问号字符 063
  • \' 单引号字符 039
  • \" 双引号字符 034
  • \0 空字符(NULL) 000
  • \ddd 任意字符 三位八进制
  • \xhh 任意字符 二位十六进制

不过我们在使用的时候直接加着r即可。

元字符'()'

元字符用于分组,匹配到的也是分组中的内容

>>> re.findall('y(\d+)','why123')
['123']

分组下边会在findall,search和match的区别中详细说

findall,search和match的区别

首先方法返回值

search和match会生成match对象

findall

>>> re.findall('y(\d+)','why123')
['123']
>>> re.findall('y(\d+)','why123y456')
['123', '456']

for python2

>>> re.search('y(\d+)','why123')
<_sre.SRE_Match object at 0x7fa4d89acf30>

for python3

>>> re.search('y(\d+)','why123')
<_sre.SRE_Match object; span=(2, 6), match='y123'>

可以看到search返回的是一个match对象

match

python2

>>> re.match('y(\d+)','why123')
>>> re.match('why(\d+)','why123')
<_sre.SRE_Match object at 0x7f34f78c1eb8>

python3

>>> re.match('y(\d+)','why123')
>>> re.match('why(\d+)','why123')
<_sre.SRE_Match object; span=(0, 6), match='why123'>

可以看到search返回的是一个match对象,但是与search不同的是,match只能在开头进行搜索

使用search和match的返回值

  • group()返回被正则匹配的字符串,group可以有参数
  • start()返回匹配的开始的位置
  • end()返回匹配结束的位置
  • span()返回一个元组,包含起始位置和结束位置
  • groups()返回被正则匹配的所有字符串
  • groupdict()返回被正则匹配的字典

group()

>>> re.search('([0-9]+)([a-z]+)([0-9]+)','123why456')
<_sre.SRE_Match object at 0x7f34f7945750>
>>> re.search('([0-9]+)([a-z]+)([0-9]+)','123why456').group()
'123why456'
>>> re.search('([0-9]+)([a-z]+)([0-9]+)','123why456').group(0)
'123why456'
>>> re.search('([0-9]+)([a-z]+)([0-9]+)','123why456').group(1)
'123'
>>> re.search('([0-9]+)([a-z]+)([0-9]+)','123why456').group(2)
'why'
>>> re.search('([0-9]+)([a-z]+)([0-9]+)','123why456').group(3)
'456'

groups()

>>> re.search('([0-9]+)([a-z]+)([0-9]+)','123why456').groups()
('123', 'why', '456')

groupdict()

>>> re.search('(?P<n1>[0-9]+)(?P<n2>[a-z]+)(?P<n3>[0-9]+)','123why456').groupdict()
{'n1': '123', 'n2': 'why', 'n3': '456'}
>>> re.search('(?P<n1>[0-9]+)(?P<n2>[a-z]+)([0-9]+)','123why456').groupdict()
{'n1': '123', 'n2': 'why'}

可以看到对只有指定了key的才能被groupdict()加入到字典中。

findall,search和match的分组

findall

>>> re.findall('(why)*','whywh123')
['why', '', '', '', '', '', '']

这里首先findall从开始的w进行匹配,然后匹配到why,直接添加到列表中,然后从y后开始匹配,对y后wh123的w进行匹配,没有匹配结果,返回一个''到字典中,然后是y,1,2,3,最后还有还会对空匹配,返回一个''。

>>> re.findall('(why)*','whywhy123')
['why', '', '', '', '']

这里会匹配到whywhy,但是由于分组的原因,()中为why,所以只显示括号内的why,但是,这个why是whywhy中后边的why,而不是前面的why,可以参考下边的例子

>>> re.findall('(\w){3}','why')
['y']
>>> re.findall('(\w)(\w)(\w)','why')
[('w', 'h', 'y')]
>>> re.findall('(\w)*','why')
['y', '']

不过对于*就会出现留空的问题

>>> re.findall('\d\w\d','1w2h3y4w5h6y')
['1w2', '3y4', '5h6']
>>> re.findall('(\d\w\d)','1w2h3y4w5h6y')
['1w2', '3y4', '5h6']
>>> re.findall('w\w+','why wxx wyy wzz')
['why', 'wxx', 'wyy', 'wzz']
>>> re.findall('(w\w+)','why wxx wyy wzz')
['why', 'wxx', 'wyy', 'wzz']
>>> re.findall('w(\w+)','why wxx wyy wzz')
['hy', 'xx', 'yy', 'zz']
>>> re.findall('w(\w+)y','why wxx wyy wzz')
['h', 'y']

可以看到()中的才会成为列表中的元素,但是如果有多个()列表中的元素就会转化成元组

>>> re.findall('(w)(\w+)','why wxx wyy wzz')
[('w', 'hy'), ('w', 'xx'), ('w', 'yy'), ('w', 'zz')]

如果()为嵌套的

>>> re.findall('((w)(\w+))','why wxx wyy wzz')
[('why', 'w', 'hy'), ('wxx', 'w', 'xx'), ('wyy', 'w', 'yy'), ('wzz', 'w', 'zz')]

search

>>> re.search('(why)*','whywh123')
<_sre.SRE_Match object at 0x7fa4d89b0030>
>>> re.search('(why)*','whywh123').group()
'why'
>>> re.search('(why)*','whywhy123').group()
'whywhy'
>>> re.search('w\w+','why wxx wyy wzz').group()
'why'
>>> re.search('w\w+','why wxx wyy wzz').groups()
()
>>> re.search('(w\w+)','why wxx wyy wzz').group()
'why'
>>> re.search('(w\w+)','why wxx wyy wzz').groups()
('why',)
>>> re.search('w(\w+)','why wxx wyy wzz').group()
'why'
>>> re.search('w(\w+)','why wxx wyy wzz').group(0)
'why'
>>> re.search('w(\w+)','why wxx wyy wzz').group(1)
'hy'
>>> re.search('w(\w+)','why wxx wyy wzz').groups()
('hy',)
>>> re.search('((w)(\w+))','why wxx wyy wzz').group()
'why'
>>> re.search('((w)(\w+))','why wxx wyy wzz').group(0)
'why'
>>> re.search('((w)(\w+))','why wxx wyy wzz').group(1)
'why'
>>> re.search('((w)(\w+))','why wxx wyy wzz').group(2)
'w'
>>> re.search('((w)(\w+))','why wxx wyy wzz').group(3)
'hy'
>>> re.search('((w)(\w+))','why wxx wyy wzz').groups()
('why', 'w', 'hy')
>>> re.search('(?P<n1>(?P<n2>w)(?P<n3>\w+))','why wxx wyy wzz').groupdict()
{'n1': 'why', 'n2': 'w', 'n3': 'hy'}

match

>>> re.match('(why)*','whywh123')
<_sre.SRE_Match object at 0x7fa4d89acf30>
>>> re.match('(why)*','whywh123').group()
'why'
>>> re.match('(why)*','whywhy123').group()
'whywhy'
>>> re.match('(123)*','whywh123')
<_sre.SRE_Match object at 0x7fa4d89acf30>
>>> re.match('(123)*','whywh123').group()
''

match和search的用法一直,只不过是从开通进行匹配,而match的group功能django的路由系统中。

正则表达式的贪婪匹配

python默认的正则表达式默认是以贪婪的匹配方式,可以加个?

>>> re.findall('y(\d+)','why123')
['123']
>>> re.findall('y(\d+?)','why123')
['1']
>>> re.findall('y(\d*)','why123')
['123']
>>> re.findall('y(\d*?)','why123')
['']
>>> re.findall('y(\d?)','why123')
['1']
>>> re.findall('y(\d??)','why123')
['']

特殊情况,如果前后有限制条件的时候这时贪婪模式并不适用

>>> re.findall('y(\d*?)w','why123why')
['123']

re模块的其他方法

compile()

re.compile(strPattern[,flag]) 这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式生成Pattern对象,第二个参数为Flag为匹配模式,取值可以是're.I | re.M'同时生效。

>>> p = re.compile(r'\d')
>>> print p.findall('123why456')
['1', '2', '3', '4', '5', '6']

sub()

用于字符串替换 re.sub(pattern,repl,string,max=0)

>>> re.sub("w.y","wanghongyu","why and wxy and wcy")
'wanghongyu and wanghongyu and wanghongyu'
>>> re.sub("w.y","wanghongyu","why and wxy and wcy",2)
'wanghongyu and wanghongyu and wcy'
>>> re.sub("w.y","wanghongyu","why and wxy and wcy",3)
'wanghongyu and wanghongyu and wanghongyu'

还有一个subn,可以返回替换了几次

>>> a,b = re.subn('\d','a','1w2h3y4w5h6y')
>>> print a
awahayawahay
>>> print b
6
>>> print re.subn('\d','a','1w2h3y4w5h6y')
('awahayawahay', 6)

在这里a, b = re.subn('\d','a','1w2h3y4w5h6y')等价于a = re.subn('\d','a','1w2h3y4w5h6y')[0]和b = re.subn('\d','a','1w2h3y4w5h6y')[1]

split()

正则匹配分割字符串

方法

def split(pattern, string, maxsplit=0, flags=0)

示例

>>> re.split("\d+","1why2why3why4why")
['', 'why', 'why', 'why', 'why']

可以通过指定maxsplit来指定最多分割几次

finditer()

finditer和findall匹配的方式是一样的,不同的是,finditer返回的是一个match对象的迭代器

>>> w = re.finditer('\d+','12 why44fdkjklfsa84jkklsajf12')
>>> for  match in w:
...     match.group(),match.span()
... 
('12', (0, 2))
('44', (6, 8))
('84', (17, 19))
('12', (27, 29))

标志位

re.match(parttern,string,flags=0)

flags为编译的标志位,用于修改正则表达式的匹配方式,re.I使匹配不区分大小写,re.L做本地化识别(locale-aware)匹配,re.M多行匹配,影响$^不生效,re.S使.匹配包含换行符在内的所有字符

re.S

使.匹配包括换行在内的所有字符

>>> re.findall('.','why\nwhy')
['w', 'h', 'y', 'w', 'h', 'y']
>>> re.findall('.','why\nwhy',re.S)
['w', 'h', 'y', '\n', 'w', 'h', 'y']

re.U

根据Unicode字符集解析字符,受影响\w,\W,\b

re.X

该标志通过给予更灵活的格式

re.I

忽略大小写

re.M

多行匹配

>>> re.findall(r"^why\d+","why123\nwhy123")
['why123']
>>> re.findall(r"^why\d+","why123\nwhy123",re.M)
['why123', 'why123']

当然把^去掉也能匹配所有

>>> re.findall(r"why\d+","why123\nwhy123")
['why123', 'why123']

示例

匹配一个IP

>>> re.search(r'(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d?|2[0-4]\d|25[0-5])','121.42.37.139')
<_sre.SRE_Match object; span=(0, 13), match='121.42.37.139'>

([01]?\d?\d|2[0-4]\d|25[0-5])这个括号是用来确认'|'的判定范围的,如果不加就会认为[01]?\d?\d,2[0-4]\d或25[0-5].为或的关系了

一些其他的方法

>>> re.findall('wang(hong|huang)yu','wanghongyu')
['hong']
>>> re.findall('wang(?:hong|huang)yu','wanghongyu')
['wanghongyu']