1 2 3 4

【python之路25】正则表达式

一、正则表达式简介

就其本质而言,正则表达式(或RE)是一种小型的、高度专业化的(在python中),它内嵌在python中,并通过RE模块实现。正则表达式编译成一系列字节码,然后由用C编写的匹配引擎执行。

可以用正则表达式测试工具进行测试》

二、字符匹配(普通字符、元字符)

普通字符:大多数字符和字母都会和自身匹配

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall('alex','faljfaljflajalexaa')
print(li) #打印输出['alex']

元字符的作用:

.    ----代表除了换行符以外的其他任何字符

^   ----代表以后面的字符开始

$   ----代表以前面的字符结束

[]  -----字符集,[a-z]代表小写字母从a到z的任何一个字母,[0-9]代表0-9的任何一个数字,[.]代表字符.,[a9]代表a或9,[^a-z]代表除了a-z的其他任何字符

 \  ------反斜杠后面跟元字符,去掉特殊功能变为普通字符;后面跟普通字符实现特殊功能;

\数字引用序号对应的字组所匹配的字符串,例如:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.search(r'(alex)(eric)com\2','alexericcomeric').group()
print(li) #alexericcomeric,其中\2代表的是第2组括号

\d 匹配任何十进制数字,相当于[0-9]

\D 匹配任何非数字字符,相当于[^0-9]

回车符(\r)、换行符(\n)、水平制表符(\t)、垂直制表符(\v)、换页符(\f)

\s 匹配任何空白字符,相当于[ \t\n\r\f\v]

\S 匹配任何非空白字符,相当于[^ \t\n\r\f\v]

\w 匹配任何字母数字字符,相当于[0-9a-zA-Z_]

\W 匹配任何非字母数字字符,相当于[^0-9a-zA-Z_]

\b 匹配一个单词和边界,也就是单词和空格间的位置,作用:就是你在匹配整个单词的时候,如果不是整个单词就不匹配I,很多单词里都有I的,这时候用\bI\b就表示匹配整个单词I,而不是单词中包含的I.

例如:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall(r'\b[a-z]+\b','how are you')
print(li) #\b是字母与空格之间的边界

如下面例子,有张三和张三丰,想匹配张三就可以用边界匹配:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall(r'张三\b','张三 张三丰  李四 李四光 张三杰 张三')
print(li) #打印输出:['张三', '张三'],如果没有\b那么匹配出4个张三

|  表示或者,x|y表示匹配x或者y,例如:匹配IP地址

#!usr/bin/env python
# -*- coding:utf-8 -*-
#匹配IP地址
import re
li = re.search(r'(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d|2[0-4]\d|25[0-5])','192.168.1.12').group()
print(li)

元字符之量词:

*   -----代表任意个字符,0-多个字符

+  -----代表1-多个字符

?   ------代表0-1个字符

{} ------代表重复固定次数,如:{3}重复3次,{3,}大于等于3次,{3,5}重复3-5次,{,3}重复0-3次

三、贪婪模式与最少匹配模式(?)

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall('alex*','ajalexxxxxaa')
print(li) #打印输出['alexxxxx'],匹配多个字符时通常默认为贪婪模式

数量元字符后面加上?可以变为最少匹配模式,例如:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall('alex*?','ajalexxxxxaa')
print(li) #打印输出['ale'],*后面加上?,切换为最少匹配模式

但当两边字符都能匹配上,中间加?,不会变为最少匹配模式

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall('a\d+?b','a23b')
print(li) #打印输出['a23b']

 四、正则表达式re的函数

1、match函数(只能匹配开始的字符,并且只匹配一次)

用法:re.match(pattern,string,flags=0)  ----pattern正则表达式字符串,string,正则表达式作用于的字符串,flags编译标志位,用于修改正则表达式的匹配方式,例如大小写、多行匹配等。主要有以下选项:

re.I    IGNORECASE忽略大小写,使匹配的大小写不敏感

re.X   VERBOSE忽略空格,可以为方便观看而做注释用。

re.M   MULTILINE多行匹配,影响^和$

re.S   DOTALL使.匹配换行符在内的所有字符,例如:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li1 = re.findall('.','abc\nde')  #['a', 'b', 'c', 'd', 'e'] 
li2 = re.findall('.','abc\nde',re.S) #['a', 'b', 'c', '\n', 'd', 'e']
print(li1,li2)

一旦匹配成功会返回一个match object对象,该对象的方法如下:

group()  ---返回re匹配的字符串

start()   ----返回匹配开始的位置

end()   ------返回匹配结束的位置

sapan()  -----返回匹配一个元组包含匹配(开始,结束)的位置

group()  -----返回re整体匹配的字符串,可以一次输入多个组号

groups()  -----返回一个元组,元组中包含所有分组结果

groupdict()  -----返回一个字典,字典中包含所有的分组结果

1)group() 返回re整体匹配的字符串,相当于group(0)

 

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group()
print(li)  #打印输出123abc456

 

2) group(n,m)  返回元组,组号为n,m所匹配的字符串,如果组号不存在,则抛出IndexError异常

 

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(1,2)
print(li)  #打印输出('123', 'abc')

 

也可以group(n) 返回组号所匹配的字符,例如

 

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li0 = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(0)  #123abc456
li1 = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(1)  #123
li2 = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(2)  #abc
li3 = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').group(3)  #456
print(li0,li1,li2,li3)

 3)groups() 返回元组,将所有组号匹配到的字符串以元组的形式返回,通常groups不需要参数。

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.search('([0-9]*)([a-z]*)([0-9]*)','123abc456').groups()
print(li)  #打印输出('123', 'abc', '456')

4)group,groups,groudict的用法及区别,如:下面三个例子:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
str = 'hello how are you!'
r = re.match('h\w+',str)
print(r.group()) #打印输出:hello
print(r.groups()) #打印输出:()
print(r.groupdict()) #打印输出:{}

 

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
str = 'hello how are you!'
r = re.match('(h)(\w+)',str)
print(r.group()) #打印输出:hello
print(r.groups()) #打印输出:('h', 'ello')
print(r.groupdict()) #打印输出:{}
#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
str = 'hello how are you!'
r = re.match('(?P<a1>h)(?P<a2>\w+)',str)
print(r.group()) #打印输出:hello
print(r.groups()) #打印输出:('h', 'ello')
print(r.groupdict()) #打印输出:{'a1': 'h', 'a2': 'ello'}

 

2、search函数(浏览全部字符串,匹配第一个符合规则的字符串)

参数与match完全相同

3、findall函数(匹配所有符合的字符,匹配多次) finditer()函数

findall,如果用到组,那么优先返回组内容,如果想返回所有内容,则需要在组前面加?:,例如:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall('www.(baidu|laonanhai).com','www.baidu.com')
print(li)  #打印输出:['baidu']
#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall('www.(?:baidu|laonanhai).com','www.baidu.com')
print(li)  #打印输出:['www.baidu.com']

findall函数返回一个列表,finditer返回的是一个迭代器

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
iter_object = re.finditer('\d','one1two2three3four4')
print(iter_object)  #<callable_iterator object at 0x000001CF23FBA8D0>
for i in iter_object:
    print(i.group(),i.span())
#打印输出
    # 1(3, 4)
    # 2(7, 8)
    # 3(13, 14)
    # 4(18, 19)

分组如果是量词为*则可以匹配到空字符,例如:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
n = re.findall('(\dasd)*','lasd2asdp3asd98kif')
print(n) #['', '', '', '', '2asd', '', '3asd', '', '', '', '', '', '']
import re
n = re.findall('(\dasd)+','lasd2asdp3asd98kif')  #如果能多次匹配则显示最后一次匹配,lasd2asd匹配到的是2asd
print(n) #['2asd', '3asd']

分组括号内部加":?"可以取消分组,例如:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
n = re.findall('(?:\dasd)+','1asd2asdp3asd98kif')  #分组内部加?:可以取消分组效果
print(n) #['1asd2asd', '3asd']

4、sub与subn函数

re.sub(pattern,repl,string,max=0)

参数:pattern正则表达式字符串,repl替换为的字符串,string要查找替换的字符窜,max=0 全部替换,1从左到右替换1个,2从左到右替换两个.......

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.sub('g.t','have','I get A,I got B,I gut C')
print(li)  #打印输出 have A,I have B,I have C

 subn函数返回一个元组,元组中包含两个元素,第1个元素是替换后的字符串,第2个元素为替换次数,例如:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.subn('g.t','have','I get A,I got B,I gut C')
print(li)  #打印输出 ('I have A,I have B,I have C', 3)

 5、compile函数

re.compile(strPattern[,flag])

这个方法是pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象,第2个参数为编译标志位,可以利用,re.I等。

把常用的正则表达式编译为Pattern对象,可以被反复调用,从而提高效率。

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
text = 'JGood is a hansome boy,he is cool,clever.'
regex = re.compile(r'\w*oo\w*')
print(regex.findall(text)) #打印输出['JGood', 'cool']

 6、split函数 ----分割函数

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.split(r'\d','one1two2three3four4')
print(li) #打印输出['one', 'two', 'three', 'four', '']

也可以结合copile函数:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
p = re.compile(r'\d')
li = p.split('one1two2three3four4')
print(li) #打印输出['one', 'two', 'three', 'four', '']

 

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.split('[ab]','cabd')
print(li)  #['c', '', 'd'],注意空字符串是连续分割的时候产生的

 split可以支持分组分隔,如下两个例子:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
st = 'hello alex bcd abcd lge acd 19'
n = re.split('a\w+',st,1)
print(n)  #['hello ', ' bcd abcd lge acd 19']
#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
st = 'hello alex bcd abcd lge acd 19'
n = re.split('a(\w+)',st,1)
print(n)  #['hello ', 'lex', ' bcd abcd lge acd 19']

 

五、关于rawstring(原生字符串)以及  \符

 \  ------反斜杠后面跟元字符,去掉特殊功能变为普通字符;后面跟普通字符实现特殊功能;

python反斜杠中作用:

\n 表示换行符, ASCII码是10

\r 表示回车符,ASCII码是13

\t 制表符

\b 表示退格字符

#!usr/bin/env python
# -*- coding:utf-8 -*-
f = open(r"D:\abc.txt")

如果去掉r用:

f = open(r"D:\abc.txt")
则会报错,因为\a表示特殊意义。
\a是转义字符007,表示响铃符DEL
#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall('\\\\','ab\cd')
print(li)

分析一下“\\\\”,第一个斜杠是转义符,第二个斜杠是斜杠本身,第三个斜杠是转义符,第四个斜杠是斜杠本身。 
有2点要清楚: 
1.字符串里面表示斜杠就需要两个斜杠如“\\” 
2.正则表达式里的斜杠需要转意,是用“\\”标示。 
这样就比较好解释: 
我们先要表示正则表达式里面的斜杠“\\”,然后再用字符串表示出来。而这2个斜杠分别需要一个转义符,这样就成了4个斜杠在正则表达式里面表示 一个斜杠

如果用原生字符串r,两个双斜杠就可以达到目的:

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall(r'\\','ab\cd')
print(li)

第2个斜杠表示:python原生的字符串\,传给正则后,正则用原生的\进行转义。

import re
R = re.match(r'\bblow','blow') #或者用\\b,python字符串中\b表示退格,所以传给正则时并不是\b而是退格,
##所以python字符串中需要转义把\b原值传给正则表达式
#正则表达式把\b翻译为元字符单词边界
print(R)

 六、其他及补充用法

1、分组,如下面数据,匹配abc一次货多次的

abcff

abcabcaadd

abcabcabc

re: (abc)+

2、|的用法

张三|二丰   ----表示匹配张三或二丰

张(三|二)丰   ----表示匹配张三丰或张二丰

3、后向引用与非捕获,分组的延续引用

后向引用:

前面日期是入职日期,后面日期是离职日期,找出当年入职当年离职的日期

正则表达式:(\d{4}).+\1.+

\1代指(\d{4}),并且与(\d{4})匹配的值完全相同

在职日期
2009-9-10 2009-10-4
2008-9-10 2010-10-4
2003-9-10 2003-11-4
2001-3-10 2001-6-4
1998-3-4 2012-1-1
2012-10-1 2012-10-29
2005-3 2005-8
2004-2-13 2005-2-3
2001年2月到2001年9月

 非捕获分组:分组默认的情况是有匹配组的,如果不想要匹配组那么可以在括号里面的最前面加?:,这样就可以去掉匹配组

例如:(?:\d{4}).+\1.+

4、[]的用法

[]表示括号中选中的若干字符之一

[xyz]字符集合,匹配包含的任意一个字符

[^xyz]负值字符集合。匹配未包含的任意一个字符

[a-z]字符范围,匹配制定范围内的任意字符

[^a-z]负值字符范围,匹配任何不在范围内的任意字符

[0-9]字符范围,匹配制定范围内的任意字符

[^0-9]负值字符范围,匹配任何不在范围内的任意字符

例如:匹配单个单词

How are you

正则表达式:[a-zA-Z]+

5、字符组去编号

正则表达式:\d+-\d+[、. ]?

1-11.判断语句之IF
1-12、判断语句IF之多条件
1-13判断语句之SELECT
1-14、循环语句之DO...LOOP
1-15、循环语句之DO...LOOP实例
1-16循环语句之DO WHILE...LOOP
1-2、宏在工作中的运用
1-3、Excel VBA基础
13-4、Excel VBA窗口介绍
1-6.对象
1-8、方法
341-5、Excel VBA代码编写规则
434-7、属性
81-9、常量、变量

6、首尾锚定

^   ----匹配字符串的开始位置

$   ----匹配字符串的结束位置

例如:以大写字母开头数字结尾的字符串

正则表达式:^[A-Z].*\d$

7、汉字匹配

\un  匹配 n,其中n是四位十六进制数表示的Unicode字符

汉字范围的第一个字符是“一”,其编码为:\u4e00;最后一个字符为龢,其编码为:\u9fa5

汉字范围为:[一-龢]

例如:提取字符串中的汉字

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
li = re.findall('[一-龢]','苹果apple橘子orange')
print(li) #打印输出:['苹', '果', '橘', '子']

 8、零宽断言

正向零宽断言,从左到右查看匹配:(?=...)

负向零宽断言,与正向零宽断言相反,从左到右查看不匹配的位置:(?!...)

零宽断言实际匹配的是一个位置,并不是实际字符或字符串。

正则表达式:(?=I)    ----匹配到I前面的位置

正则表达式:(!=I)    ----除了I前面的位置,其他位置都匹配到了

 

例1:在参赛选手省份简称前面加(中)

正则表达式:(?=[川吉云粤])

参赛选手国籍 修改后
李四-川            李四-(中)川
陈升东-吉         陈升东-(中)吉
竹下君-日本      竹下君-日本
梁汉升-云         梁汉升-(中)云
张三-川            张三-(中)川
刘心兵-云         刘心兵-(中)云
龙心-粤            龙心-(中)粤
朴志熙-韩国      朴志熙-韩国
成龙国-粤         成龙国-(中)粤

例2:职务带总字的加上“高管”

正则表达式::(?=副?总)   ---替换为:“:高管”

张三:总经理              张三:(高管)总经理
李四:车间主任           李四:车间主任
陆一:总裁                 陆一:(高管)总裁
周同生:经理              周同生:经理
欧阳小小:副总经理      欧阳小小:(高管)副总经理
林汤圆:主管              林汤圆:主管
张山:副经理              张山:副经理

例3:提取下面字符串中的金额

正则表达式:\d+\.?\d*(?=[元块])

买2件衣服:600元,买12袋零食:89.5元,打游戏:98.5元
买3本书:97块,买1双鞋子:408元,买日用品:389.7元
买化妆品:305元,买1辆单车:670元,买1支笔:8元
5个朋友过生日送礼物费用895.9元,买了1只小猫200块
日用品:200元,请客吃饭:590元,借给朋友2000块,丢了100元。

 例4、负向零宽断言,给每个单子中间加-

正则表达式:(?!^)(?=[a-z])

pipe 管                 p-i-p-e 管

9、懒惰与贪婪模式

一般元字符量词都是贪婪模式,如果想变为懒惰模式则在元字符量词后面加?

?的用法:

1)表示量词{0,1}

2)表示非捕获型的匹配模式(?:)

3)表示零宽断言(?=)  (?!)

4)表示切换为懒惰模式:+?

例如:字符串abcdef

正则表达式:[a-z]+    匹配结果:[abcdef]

正则表达式:[a-z]+?  匹配结果:[a,b,c,d,e,f]

10、分组的妙用

例1:下面字符串,提取部门和人数:

财务部 26人 业务部 4人 回收站 2人 人事科 34人 生产车间 4567人

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
st = '财务部 26人 业务部 4人 回收站 2人 人事科 34人 生产车间 4567人'
li = re.findall('([一-龢]+) (\d+人)',st)
print(li)
#打印输出:
#[('财务部', '26人'), ('业务部', '4人'), ('回收站', '2人'), ('人事科', '34人'), ('生产车间', '4567人')]

例2:下面字符串,提取姓名、身份证号、性别、年龄、籍贯

丁红梅 130981198206188284 女 30 河北省 沧州市 泊头市 蔚然 632324196704122182 女 45 青海省 黄南藏族自治州 河南蒙古族自治县 宓香菱 371103196505169263 女 47 山东省 日照市

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
st = '丁红梅 130981198206188284 女 30 河北省 沧州市 泊头市 蔚然 632324196704122182 女 45 ' \
     '青海省 黄南藏族自治州 河南蒙古族自治县 宓香菱 371103196505169263 女 47 山东省 日照市 '
li = re.findall('(\S+) (\S+) (\S) (\d+)(( \S+){1,3})',st)
print(li)
#打印输出:
#[[('丁红梅', '130981198206188284', '女', '30', ' 河北省 沧州市 泊头市', ' 泊头市'), 
# ('蔚然', '632324196704122182', '女', '45', ' 青海省 黄南藏族自治州 河南蒙古族自治县', ' 河南蒙古族自治县'), 
# ('宓香菱', '371103196505169263', '女', '47', ' 山东省 日照市', ' 日照市')]

 例3:分组包含分组的情况

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
st = 'hello alex alex bac abcd 19'
r = re.findall('((a)(\w+))',st)
print(r)  #[('alex', 'a', 'lex'), ('alex', 'a', 'lex'), ('ac', 'a', 'c'), ('abcd', 'a', 'bcd')]

 例4:利用正则表达式计算:(参考split分组)

1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )的值

 

#!usr/bin/env python
# -*- coding:utf-8 -*-
import re
def calculate(exp):
    return eval(exp)

st = '1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )'
while True:
    print(st)
    li = re.split('\(([^()]+)\)',st,1)
    if len(li) == 3:
        # left = li[0]
        # middle = li[1]
        # right = li[2]
        left,middle,right = li  #本句代码相当于上面的三句注销的代码
        middle_result = calculate(middle)
        st = left + str(middle_result) + right
    else:
        result = calculate(st)
        print(result)
        break
#!usr/bin/env python
# -*- coding:utf-8 -*-
n1,n2 = 3,4
print(n1,n2)  #3 4

li = [11,22,33]
n1,n2,n3 = li
print(n1,n2,n3)  #11 22 33

 

posted @ 2017-02-13 23:58  I我的博客I  阅读(822)  评论(0编辑  收藏  举报