正则表达式

概述

  • 可以用来判断某个字符串是否符合某种模式

    • 比如是否是邮箱地址、网址、电话号码、身份证号
  • 可以用来在文本中寻找并抽取符合某种模式的字符串

    • 比如在某个文本中抽取出邮箱地址
    • 比如在小说中,抽取孔明说的有提及曹操的话

定义:正则表达式是个某些字符有特殊含义的字符串

表示一种字符串的模式

"abc" #匹配"abc"

"b.?p.*k" #可以匹配"bapk","bp abx k"等多种符合b**p**k的样子

"\d{3}[a-zA-Z]+.(\d{2}|N/A)\s\1"#???
#可以使用相关函数求给定字符串和正则表达式的匹配情况
字符/组合匹配的模式正则表达式样例eg
.除‘\n’外的任意一个字符,包括汉字 (多行匹配方式下也能匹配‘\n’)‘a.b’‘acb’.‘adb’…
*量词。表示左边的字符必须出现0次或任意多次‘a*b’‘b’,‘aab’…
?量词。表示左边的字符必须出现0次或1次‘ka?b’‘kb’,‘kaaab’
+量词。表示左边的字符必须出现1次或更多次‘ka+b’‘kab’,‘kaab’
{m}量词。m是整数。表示左边的字符必须并且只能出现m次‘ka{3}b’‘kaaab’
{m,n}量词。m、n是整数。表示左边的字符必须并且至少出现m次,做多n次。n可以忽略不写,表示无上限‘ka{1,3}b’‘kab’,‘kaab’,‘kaaab’
\d一种数字字符,等价于[0-9]‘a\db’‘a0b’,‘a1b’…
\D一种非数字字符,等价于[ ^ \d ] [ ^ 0-9 ]‘a\Db’‘acb’,‘a9b’…
\s一种空白字符,比如空格,\t等‘a\sb’‘a b’,‘a\nb’…
\S一种非空白字符’a\Sb‘‘akb’…
\w一种单词字符,包括汉字或大小写字母,数字,下划线或其他语言的汉字‘a\wb’‘a_b’,‘a中b’…
\W一种不是单词字符的字符‘a\Wb’‘a?b’…
|A|B表示能匹配A或能匹配B均能算匹配‘ab|c’‘ab’,‘c’

如果要在正则表达式中表示特殊字符本身,就应该在其前面加’ \ ';

正则表达式匹配的字符串
‘a\ $b’‘a$b’
‘a\ *b’‘a*b’
‘a\ [\ ]b’‘a[]b’
‘a\ .*b’‘ab’ ‘a.b’ ‘a…b’…
‘a\ \ \ \b’‘a\b’ (注意:此字符串长度为3,中间那个字符是’‘,即r’a\b’)
r’a\ \b’r’a\b’ (r’a\b’等价于’a\ \b’)

字符范围和量词

范围符号[] 和 量词

[XXX]用法:

  • 此处必须出现一个某某范围内的字符
  • 此处必须出现一个字符,但不可以是某某范围内的字符
[a2c]匹配’a’,‘2’,'c’之一
[a-zA-Z]匹配任一英文字母
[\da-z?]匹配一个数字或小写英文字母或’?’
[^abc]匹配一个非’a’,‘b’,'c’之一 的字符
[^a-f0-3]匹配一个非英文字母’a’ 到’f’,也非数字’0’到’3’的字符

匹配汉字

汉字的unicode编码范围是4e00-9fa5(16进制),因此【\ u 4e00-\ u9fa5】表示一个汉字

正则表达式示例

"[1-9]\d*" #=>表示以[1-9]开头,\d可出现任意多次,故为正整数

"-[1-9]\d*" #为负整数

"-?[1-9]\d*|0" #=> -?表示负号可以出现0次或1次,以[1-9]开头,\d可出现任意多次
               #=> 或者和0相匹配
               #=> 合起来表示就是整数

"[1-9]\d*|0"   #=>表示非负整数

"-?([1-9]\d*\.\d*[1-9]|0\.\d*[1-9]|0)" #=>左右两边都没有多余0的小数

"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" #=>邮箱格式

正则表达式的函数

re.match

import re
re.match(pattern,string,flags=0)
#=>从字符串string的 起始位置匹配 一个模式pattern
#=>flags 标志位,用于控制模式串的匹配方式
    #=>如是否区分大小写,是否多行匹配等等。如 re.M|re.I 表示忽略大小写并且多行匹配
#成功则返回一个匹配对象,否则返回None
import re
def match(pattern,string):
    x=re.match(pattern,string)
    if(x!=None):
        print(x.group()) #=>x.group是匹配上的字符串
    else:
        print("None")
match("a c","a cdkgh") #=>a c
match("abc","kabc")#=>None 虽然有abc,但不是在起始位置
match("\wabc","kabc")#=>kabc
match("a\tb*c","a\tbbbbcdec")#=>a       bbbbc b可出现0次或任意多次,然后跟c
match("ab*c","ac")#=>ac
match("a\d+c","ac")#=>None 中间没有数字
match("a\d{2}c","a34c")#=>a34c
match("a\d{2,}c","a3474884c")#=>a3474884c

match(".{2}bc","bcbcdbc") #=>bcbc
match("ab.*","ab")#=>ab
match("ab.*","abcd")#=>abcd
match("\d?b.*","1bcd")#=>1bcd
match("\d?b.*","bbcd")#=>bbcd
match("a?bc.*","abbbcd") #=>None
match("a.b.*","abcd") #=>None .是必须有一个字符
match("a.b.*","aeb") #=>aeb
match("a.?b.*","aebcdf")#=>aebcdf
#a和b之间没字符或有任意一个字符均可 
match("a.+b.*","aegsfb") #=>aegsfb
match("a.+b.*","abc")#=>None
match("a高.+k","a高大kcd")#=>a高大k

re.search

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

  • 查找字符串中可以匹配成功的子串

  • 若匹配成功,则返回匹配对象。若无法匹配,则返回None

  • 与match的区别在于,可以不从开头开始匹配

import re
def search(pattern,string):
    x=re.search(pattern,string)
    if(x!=None):
        print(x.group(),x.span()) #=>输出子串以及起止位置
    else:
        print("None")
search("a.+bc*","dbaegsfbcef") #=>aegsfbc (2,9)
search("a.+bc*","bcdbaegsfbccc") #=>aegsfbccc (4,13)
search("a.?bc*d","dabccdc") #=>abccd (1,6)
search("aa","baaaa") #=>aa (1,3)
search("\([1-9]+\)","ab123(0456)(789)45ab") #=>(789) (11,16)
search("[1-9]\d+","ab01203d45")#=>1203 (3,7)

re.findall

  • re.findall(pattern,string,flags=0)
  • 查找字符串中**所有和模式匹配的子串(不重叠)**放入列表。
  • 若一个匹配子串都没有,则返回空列表
import re
print(re.findall('\d+',"this is 334 what me 774gw"))
#=>['334','774']
print(re.findall('[a-zA-Z]+',"A dog has 4 legs.这是true"))
#=>['A','dog','has','legs','true']
print(re.findall('\d+',"this is good."))
#=>[]
print(re.findall("aaa","baaaa"))
#=>['aaa']

re.finditer

  • re.finditer(pattern,string,flags=0)
  • 查找字符串中所有和模式匹配的子串(不重叠)
  • 每个子串对应与一个匹配对象
  • 返回匹配对象的序列
import re 
s = '233[32]88ab<433>(21)' 
m = '\[\d+\]|<\d+>' #=>[32] <433>
for x in re.finditer(m,s): 
    print(x.group(),x.span())
i = 0
for y in re.finditer(m,"aaaaa"): #=>匹配对象序列为空
    i += 1

re.sub

  • re.sub(模式串,替换串,母串)
  • 用于替换匹配的子串
import re 
str=re.sub('\d+',"...","abc13de4fg")
print(str) #=>abc...de...fg
print(re.sub('\d+',"","abc13de4fg"))#=>abcdefg
print(re.sub('gone','go','I gone hegone me'))#=>I go hego me

边界符号

  • \A 表示字符串的左边界,即要求从此往左边不能有任何字符
  • \Z 表示字符串的右边界,即要求从此往右边不能有任何字符
  • ^ 与\A同。但多行匹配模式下还可以表示一行文字的左边界
  • $ 与\Z同。但多行匹配模式下还可以表示一行文字的右边界
  • \b 表示此处应为单词的左边界或右边界,即不可是单词字符
  • \B 表示此处不允许是单词的左边界或右边界,即必须是单词字符
  • 边界符号本身不会和任何字符匹配。

注意

  • 正则表达式的边界符号’\b’ 是两个字符。但是在Python字符串中,‘\b’和’\t’,'\n’类 似,是一个字符(Backspace)。
  • 因此在正则表达式中使用边界符号\b,要写 ’ \ \b’ 。如果写 ‘\ \ \ \b’,则连续的两个’‘被看作是一个普通的’‘,不会和后面的’b’一起被 当作字符组合,变成边界符号’\b’。
  • print(“\b”) #>>\b
import re
def search(pattern,string):
    x=re.search(pattern,string)
    if(x!=None):
        print(x.group(),x.span()) #=>输出子串以及起止位置
    else:
        print("None")
pt="ka\\b.*" #=>表示a的右边不能是单词字符
search(pt,"ka") #=> ka
search(pt,"kax") #=> None
search(pt,"ka?d") #=>ka?b
pt = ".*\\bka\\b" #=>k的左边不能是单词字符,a的右边也不能是单词字符
search(pt,"ka") #=>ka
search(pt,"ska?") #=>None
search(pt,"b?ka?") #=>b?ka?
import re
def search(pattern,string):
    x=re.search(pattern,string)
    if(x!=None):
        print(x.group(),x.span()) #=>输出子串以及起止位置
    else:
        print("None")
m = r"\bA.*N\b T" #等价于 m = "\\bA.*N\\b T"
search(m,"Ass$NC TK") #=>None
search(m,"this Ass$N TK") #=>Ass$N T
m = "\BA.*N\B\w T" 
search(m,"this Ass$N TK") #=>None
search(m,"thisAss$NM TK") #=>Ass$NM T
search(m,"Ass$NM TK") #=>None
search(m,"xAss$NM TK") #=>Ass$NM T
pt = "\\b高兴" 
search(pt,"我高兴") #=>None
search(pt,"我 高兴") #=>高兴

分组

形式:(…)

  • 括号中的表达式是一个分组。多个分组按左括号从左到右从1开始依次编号
import re
x = re.search('[a-z]+(\d+)[a-z]+',"ab 123d hello553world47") 
print(x.group())#=>hello553world 完整的匹配对象
print(x.group(1))#>>553 一号分组内容,忽略掉其他
m = "(((ab*)c)d)e" 
r = re.match(m,"abcdefg") 
print(r.group(0)) #=> 0号分组是 完整的匹配对象 abcde
print(r.group(1)) #=> 1号分组 abcd
print(r.group(2)) #=> abc
print(r.group(3)) #=> ab
print(r.groups()) #=> 所有分组情况,按元组的类型给出
#=>('abcd', 'abc', 'ab')
import re 
m = "(ab*)(c(d))e" 
r = re.match(m,"abcdefg") 
print(r.groups())#>>('ab', 'cd', 'd')
print(r.group(0)) #>>abcde
print(r.group(1)) #>>ab
print(r.group(2)) #>>cd
print(r.group(3)) #>>d

在分组的右边可以通过分组的编号引用该分组所匹配的子串

import re 
m = r'(((ab*)c)d)e\3'#r表示字符串里的'\'不再转义
#要求 ab*cde后面跟着3号分组在本次匹配中匹配上的子串 
r = re.match(m,"abbbcdeabbbkfg") 
# 红色部分少一个b则不能匹配 
print(r.group(3))# abbb 
print(r.group())# abbbcdeabbb

如果后面没有三号分组可匹配的内容,就会报错

import re
pt = 'a(.)\\1*b' #或 pt = r'a(.)\1*b' 
#>>1号分组匹配上的是c
print(re.search(pt,'kacccccb').group()) #>>acccccb 
#>>1号分组匹配上的是x
print(re.search(pt,'kaxxxxb').group()) #>>axxxxb 
#>>1号分组匹配上的是x
print(re.search(pt,'kaxb').group())#>>axb
x = re.search(pt,'kaxyb') #>>None
if x != None:
    print(x.group())
  • 分组作为一个整体,后面可以跟量词
import re 
m = "(((ab*)+c)d)e" # *号是0次或任意多次
r = re.match(m,"ababcdefg")
print(r.group()) #>>ababcde 
print(r.groups()) #>>('ababcd', 'ababc', 'ab')
r = re.match(m,"abacdefg")
print(r.group()) #>>abacde!!! 
print(r.groups()) #>>('abacd','abac','a')
#=>以最后一次匹配的为准
# 分组的量词化,并不要求每次分组匹配都一样
# 比如这里,第一次匹配出ab,第二次匹配出a =>均符合ab*

re.findall和分组

  • 在正则表达式中没有分组时,re.findall返回所有匹配子串构成的列表。
  • 有且只有一个分组时,re.findall返回的是一个子串的列表,每个元素是一个匹配子串中分组对应的内容。
import re

//存在分组
m = '[a-z]+(\d+)[a-z]+' 
x = re.findall(m,"13 bc12de ab11 cd320ef")
print(x) #>>['12', '320']

//不存在分组
m='[a-z]+\d+[a-z]+'
x = re.findall(m,"13 bc12de ab11 cd320ef")
print(x) #>>['bc12de', 'cd320ef']
  • 在正则表达式中有超过一个分组时
  • re.findall返回的是一个元组的列表, 每个元组对应于一个匹配的子串,元组里的元素,依次是1号分组、2号分组、3号分组…匹配的内容
import re 
m = '(\w+) (\w+)' 
r = re.match(m,"hello world") 
print(r.group())#>>hello world
print(r.groups())#>>('hello', 'world')
print(r.group(1)) #>>hello
print(r.group(2)) #>>world
r = re.findall(m,"hello world, this is very good") 
#找出由所有能匹配的子串的 groups() 构成的元组,互相不重叠
print(r) #>>[('hello', 'world'), ('this', 'is'), ('very', 'good')]

|的用法

  • 表示“或”
  • 如果没有放到分组“()”里,则作用范围是直到整个正则表达式开头或结尾或另一个“|”
  • “\w{4}ce|c\d{3}|p\w”
  • 可以匹配:
    • “c773” #满足c\d{3}
    • “ab12ce” #满足\w{4}ce
    • “pk” #满足p\w
  • 从左往右短路匹配
    • 匹配上一个后就不计算是否还能匹配后面的
import re 
pt = "\d+\.\d+|\d+" 
print(re.findall(pt,"12.34 this is 125")) #>>['12.34', '125'] 
pt = "aa|aab" 
print(re.findall(pt,"aabcdeaa12aab"))#>>['aa', 'aa', 'aa']
  • 用于分组:作用域是分组内
import re 
m ="(((ab*)+c|12)d)e" 
tmp=re.search(m,'ababcdefgKK12deK')
print(tmp.group())#=>ababcde 12de
print(re.findall(m,'ababcdefgKK12deK')) 
#>>[('ababcd', 'ababc', 'ab'), ('12d', '12', '')] 
for x in re.finditer(m,'ababcdefgKK12deK'): 
    #=finditer返回的是匹配对象的序列
    print(x.groups())
#>>('ababcd', 'ababc', 'ab') #>>('12d', '12', None) 
m = '\[(\d+)\]|<(\d+)>' 
for x in re.finditer(m,'233[32]88ab<433>'):
    print(x.group(),x.groups()) 
#>>[32] ('32', None) #=><433> (None, '433')

贪婪匹配模式

  • 量词+ * ? {m,n} 默认匹配尽可能长的子串
import re 
print(re.match("ab*","abbbbbk").group()) #>>abbbbb

#贪婪模式下存在的问题
print(re.findall("<h3>(.*)</h3>","<h3>abd</h3><h3>bcd</h3>"))
#=> findall+分组,输出的列表是分组内的内容
#>> ['abd</h3><h3>bcd'] 贪婪模式下,认为这一串都是中间的任意字符
print(re.findall("\(.+\)","A dog has(have a).这(哈哈)true ()me"));
#>>['(have a).这(哈哈)true ()']

懒惰匹配模式

  • 量词+ * ? {m,n} 后面加‘?’ 则匹配尽可能短的子串
import re 
m = "a.*?b" 
for k in re.finditer(m,"aabab"): 
    print(k.group(),end=" ") 
    #>>aab ab


m = "<h3>.*?</h3>" 
a = re.match(m,"<h3>abd</h3><h3>bcd</h3>") 
print(a.group()) 
#>><h3>abd</h3> 

print(re.findall(m,"<h3>abd</h3><h3>bcd</h3>"))
#>>['<h3>abd</h3>', '<h3>bcd</h3>']


m = "<h3>.*?[M|K]</h3>" #[M|K]存在M或K
a = re.match(m,"<h3>abd</h3><h3>bcK</h3>")
print(a.group())
#>><h3>abd</h3><h3>bcK</h3>

匹配对象

匹配成功时返回结果(称为匹配对象)的属性

  • string: 匹配时使用的母串

  • lastindex: 最后一个被匹配的分组的编号(不是最大编号)。如果没有被匹配的分组,就None

  • group: 获得一个或多个分组匹配的字符串;指定多个参数时将以元组的形式返回

    • group(1,2,3)

    • 匹配了多次的分组,返回最后一次匹配的子串

  • group():以元组的形式返回全部分组匹配的字符串

  • groupdict(): 返回以有名字的组的名字为键,以该组匹配的子串为值的字典

  • start([group]): 返回指定的组匹配的子串在string中的起始位置。参数缺省值为0

  • end([group]):返回指定的组匹配的子串在string中的结束位置。参数缺省值为0

  • span([group]):返回-(start([group],end([group])) 缺省为0

import re
m = re.match(r'(\w+) (\w+)(.)', 'hello world!ss') 
print( m.string)
#返回匹配对象的母串 
#>>hello world!ss

print(m.lastindex)
#返回最后一个匹配上的分组的编号。因为分组可以嵌套,并不能说明一定是最大编号
#>>3


print(m.group(0,1,2,3))
#0返回的所有分组的内容
#>>('hello world!', 'hello', 'world', '!') 


print(m.groups())
#等效于m.group(0) 
#>>('hello', 'world', '!') 


print(m.start(2)) 
#>>6 
print(m.end(2))
#>>11
print(m.span(2)) 
#>>(6, 11)

应用实例

import re
f=open("文件地址","r",encoding="utf-8")
txt=f.read()
f.close()
pt="(孔明.{0,2}曰:“[^” ]*(曹操|曹贼|操贼|曹阿瞒|操).*?)"
a=re.findall(pt,txt)
print(len(a)) #>>58
for x in a:
    print(x[0])
import re 
ipadr = r"\b((25[0-5]|2[0-4]\d|((1\d{2})|([19]?\d)))\.){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\b"
mailbox = r"\b[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_]+)+\b"
url = r'http://[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][a-zA-Z0-9]{0,62})+(/[-a-zA-Z0-9]+)*\b'
s = "My ip is 223.44.3.4, this is http://www.pku.edu.cn/python/new, http://www.sohu.com my mailbox is guo_wei@pku.edu.cn. ok?"
m = re.search(ipadr,s) 
if m != None:
    print(m.group()) #>>223.44.3.4
m=re.search(mailbox,s)
if m!=None:
    print(m.group())#>>guo_wei@pku.edu.cn
for x in re.finditer(url,s):
    print(x.group())
    #>>http://www.pku.edu.cn/python/new
    #>>http://www.sohu.com

要想写出完美的正则表达式时很困难的。

可以考虑写得宽容一点。例如,ip地址一定能匹配它,但是匹配它的不一定是ip地址

后续用split以后手动判断每一段是不是小于225

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Caaaaaan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值