Python进阶学习笔记之——正则表达式

正则表达式

1、简介

正则表达式为高级的文本模式匹配、抽取、与/或文本形式的搜索和替换功能提供了基础。

Python通过模块re来支持正则表达式

提示:搜索匹配的比较

搜索:即在字符串任意部分中搜索匹配的模式 通过**search()**函数或方法实现

匹配:是指判断一个字符串能否从起始处全部或者部分地匹配某个模式 通过调用**math()**函数或方法实现

正则表达式的强大之处在于引入特殊字符来定义字符集、匹配子组和重复模式。


2、特殊符号和字符

元字符

元字符元字符含义
符号
literal匹配文本字符串的字面值literal
rel1|rel2匹配表达式rel1或者rel2,择一匹配符(管道|)
.匹配任何单一字符(除了\n)
^匹配字符串起始部分
$匹配字符串结尾部分
*匹配0次或多次前面出现的正则表达式
+匹配1次或多次前面出现的正则表达式
匹配0次或1次前面出现的正则表达式
{N}匹配N次前面出现的正则表达式
{M,N}匹配M~N次前面出现的正则表达式
[…]匹配来自字符集的任意单一字符
[x~y]匹配x~y范围内中的任意单一字符
[^…]不匹配此字符集中出现的任何一个字符,包括某一范围的字符
(*|+|?|{})?用于匹配上面频繁出现/重复出现符号的非贪婪版本(*、+、?、{})
(…)匹配封闭的正则表达式,然后另存为子组
特殊符号
\d匹配任何十进制数字,与[0-9]一致(\D与\d相反,不匹配任何非数值型的数字)
\w匹配任何字母数字字符,与[A-Za-z0-9]相同(\W与之相反)
\s匹配任意空格字符,与[\n\t\r\v\f]相同(\S与之相反)
\b匹配任何单词边界(\B与之相反)
\N匹配已保存的子组
\c逐字匹配任何特殊字符c
\A(\Z)匹配字符串的起始(结束)
扩展表示法
(?Lmsux)在正则表达式中嵌入一个或者多个特殊“标记”参数
(?:…)表示一个匹配不用保存的分组
(?P…)像一个仅由name标识而不是数字ID标识的正则分组匹配,用\g来检索
(?P=name)在同一个字符串中匹配由(?P=name)分组的之前文本
(?#…)表示注释
(?=…)匹配条件是如果…出现在之后的位置,而不使用输入字符串;称作正向前视断言
(?!..)匹配条件是如果…不出现在之后的位置,而不使用输入字符串;称作负向前视断言
(?<=…)匹配条件是如果…出现在之前的位置,而不使用输入字符串;称作正后前视断言
(?<!..)匹配条件是如果…不出现在之前的位置,而不使用输入字符串;称作负向后视断言
(?(id/name)Y|N)如果分组所提供的id或者name存在,就返回正则表达式的条件匹配Y,如果不存在,就返回N;|N可选

2.1、使用择一匹配符号匹配多个正则表达式模式

表示择一匹配的管道符号(|),也就是键盘的竖线,表示从一个“从多个模式中选择其一”。它用于分割不同的正则表达式。例如:

正则表达式模式匹配的字符串
at|homeat、home
r2d2|c3por2d2、c3po
bat|bet|bitbat、bet、bit

择一匹配有时候也称为并或者逻辑或

2.2、匹配任意单个字符

点号或者句点(.)符号匹配除了换行符\n以外的任何字符(Python正则表达式有一个编译标记[S或者DOTALL],该标记能够推翻这个限制,使点号能够匹配换行符)。例如:

正则表达式模式匹配的字符串
f.o匹配在字符“f”和“o”之间的任意一个字符:例如fao、f9o、f#o等
任意两个字符
.end匹配在字符串end之前的任意一个字符

问:怎样才能匹配句点(dot)或者句号(period)字符

答:要显示匹配一个句点符号本身,必须使用反斜杠转义句点字符,例如:“\.”。

2.3、从字符串起始或者结尾或者单词边界匹配

如果要匹配字符串的开始位置就必须使用脱字符(^)或者特殊字符\A。同样,美元符号($)或者\Z将用于匹配字符串的末尾位置

正则表达式模式匹配的字符串
^From任何以From作为起始的字符串
/bin/tcsh$任何以/bin/tcsh作为结尾的字符串
^Subject:hi$任何由单独的字符串Subject:hi构成的字符串

特殊字符\b和\B可以用来匹配字符边界。而两者的区别在于\b将用于匹配一个单词的边界,这意味着如果一个模式必须位于单词的起始部分,就不管单词前面是否有任何字符。同样,\B将匹配出现在一个单词中间的模式。例如:

正则表达式模式匹配的字符串
the任何包含the的字符串
\bthe任何以the开始的字符串
\bthe\b仅仅匹配单词the
\Bthe任何包含但并不以the作为开头的字符串

2.4、创建字符集

尽管句点可以匹配任意字符,但某些时候,可能想匹配某些特定的字符该正则表达式能够匹配一对方括号中包含的任何字符。例如:

正则表达式模式匹配的字符串
b[aeiu]tbat、bet、bit、but
[cr][23][dp][o2]一个包含四个字符的字符串,第一个要么是“c”或者“r”,以此类推

2.5、限定范围和否定

方括号中两个符号中间用连字符(-)连接,用于指定一个字符的范围。另外,如果脱字符(^)紧跟在左方括号后面,这个符号就代表不匹配给定字符集的任何一个字符

正则表达式模式匹配的字符串
z.[0-9]字母“z”后面紧跟着一个字符,然后跟着一个数字
[r-u][env-y][us]字母“r”、“s”、“t”或者“u”后面跟着“e”、“n”和“v”到“y”的任何一个字符,然后跟着“u”或者“s”
[^aeiou]一个非元音字符
[^\t\n]不匹配制表符或者\n
["-a]在一个ASCII系统中,所有字符都位于""和“a”之间

2.6、使用闭包操作符实现存在性和频数匹配

星号或者星号操作符(*)将匹配其左边的正则表达式出现零次或者多次的情况**(在计算机编程语言和编译原理中,该操作称为Kleene闭包)。加号(+)操作符将匹配一次或者多次出现的正则表达式(也叫做正闭包操作符)。问号(?)操作符将匹配零次或者一次出现的正则表达式。还有大括号操作符({}),里边的是单个值或者一对由逗号分隔的值。这将最终精确地匹配前面的正则表达式N次(如果是{N})或者一定的范围数;例如,{M,N}将匹配M~N次。例如:

正则表达式模式匹配的字符串
[dn]ot?字母“d”或者“n”,后面跟着一个“o”,然后是最多一个“t”
0?[1-9]任何数值数字,它可能前置一个“0”,例如,匹配一系列数,不管是一个还是两个
[0-9]{15,16}匹配15或者16个数字

2.7、表示字符集的特殊字符

一些特殊字符可以表示字符集。与使用“0-9”这个范围表示十进制数相比,可以简单地使用d表示任何十进制数字另一个特殊字符(\w)能够用于表示全部字母数字的字符集,相当于使用[A-Za-z0-9]的缩写形式\s可以用来表示任何空格字符这些特殊字符的大写版本表示不匹配。例如:

正则表达式模式匹配的字符串
\w±\d+一个由字母数字组成的字符串和一串由一个连接符分隔的数字
[A-Za-z]\w*第一个字符是字母;其余字符(如果存在)可以是字母或数字
\w+@\w+\.com以XXX@YYY.com格式表示的简单电子邮件地址

2.8、使用圆括号指定分组

当使用正则表达式时,一对圆括号可以实现任意一个(或两个)功能:

  • 对正则表达式进行分组;
  • 匹配子组。
正则表达式模式匹配的字符串
\d+(.\d*)?表示简单浮点数的字符串;也就是说,任何十进制数字,后面可以接一个小数点和零个或者多个十进制数字
(Mr?s?.)?[A-Z][a-z]*[A-Za-z]名字和姓氏

2.9、扩展表示法

它们是以问号开始(?..),通常用于在判断之前提供标记,实现一个前视(或者后视)匹配,或者条件检查。

正则表达式模式匹配的字符串
(?:\w+.)*以句点作为结尾的字符串,但是这些匹配不会保存下来
(?#comment)并不做匹配,只是注释
(?=.com)如果一个字符串后面跟着“.com”才做匹配操作,并不使用任何目标字符串
(?!.net)如果一个字符串后面不是跟着“.net”才做匹配操作
(?<=800-)如果字符串之前为“800-”才做匹配
(?<!192\.168\.)如果一个字符串之前不是“192.168.”才做匹配操作,假定用于过滤掉一组C类IP
(?(1)y|x)如果一个匹配组1(\1)存在,就与y匹配;否则,就与x匹配

3、正则表达式和Python语言

3.1、re模块:核心函数和方法

函数/方法功能
仅仅是re模块函数
compile(pattern,flags=0)使用任何可选的标记来编译正则表达式,然后返回一个正则表达式对象
re模块函数和正则表达式对象的方法
match(pattern,string,flags=0)尝试使用带有可选标记的正则表达式的模式来匹配字符串。如果匹配成功,就返回匹配对象;如果失败,就返回None
search(pattern,string,flags=0)使用可选标记搜索字符串中第一次出现的正则表达式模式。如果匹配成功,就返回匹配对象;如果失败,就返回None
findall(pattern,string[,flags])查找字符串中所有(非重复)出现的正则表达式模式,并返回一个匹配列表
finditer(pattern,string[,flags])与findall()函数相同,但返回的不是一个列表,而是一个迭代器。对于每一次匹配,迭代器都返回一个匹配对象
split(pattern,string,max=0)根据正则表达式的模式分隔符,split函数将字符串分割为列表,然后返回成功匹配的列表,分隔最多操作max次(默认分割所有匹配成功的位置)
re模块函数和正则表达式对象方法
sub(pattern,repl,string,count=0)使用repl替换所有正则表达式的模式在字符串中出现的位置,除非定义count,否则就将替换所有出现的位置
purge()清楚隐式编译的正则表达式模式
常用的匹配对象方法
group(num=0)返回整个匹配对象,或者编号为num的特定子组
groups(default=None)返回一个包含所有匹配子组的元组(如果没有成功匹配,则返回一个空元组)
groupdict(default=None)返回一个包含所有匹配的命名子组的字典所有的子组名称作为字典的键
常用的模块属性(用于大多数正则表达式函数的标记
re.I、re.IGNORECASE不区分大小写的匹配
re.L、re.LOCALE根据所使用的的本地语言环境通过\w、\W、\b、\B、\s、\S实现匹配
re.M、re.MULTILINE^和$分别匹配目标字符串中行的起始和结尾,而不是严格匹配整个字符串本身的起始和结尾
re.S、re.DOTALL“.”通常匹配除了\n之外的所有单个字符;该标记表示**“.”能够匹配全部字符**
re.X、re.VERBOSE通过反斜杠转义,否则所有的空格加上#都被忽略,除非在一个字符类中或者允许注释并且提高可读性

3.2、匹配对象以及 group() 和 groups() 方法

当处理正则表达式时,除了正则表达式对象之外,还有另一个对象类型:匹配对象这些是成功调用 match() 或者 search() 返回的对象匹配对象有两种主要方法:group() 和 groups()

group() 要么返回整个匹配对象,要么根据要求返回特定的子组groups() 则仅返回一个包含唯一或者全部子组的元组。如果没有子组的要求,那么当 group() 仍然返回整个匹配时,groups() 返回一个空元组。

3.3、使用 match() 方法匹配字符串

match() 是一个正则表达式对象方法。match() 函数试图从字符串的起始部分对模式进行匹配如果匹配成功,就返回一个匹配对象;如果匹配失败,就返回None匹配对象的 group() 方法可以显示那个成功的匹配

import re
m = re.match('foo','foo')
if m is not None:
    print(m.group())		#foo

模式“foo”完全匹配字符串“foo”,能够确认 m 是匹配对象:

print("\t\t\t",m)		#<re.Match object; span=(0, 3), match='foo'>

如下是一个失败的示例,将返回None:

m = re.match('bar','foo')
print(m.group())
#AttributeError: 'NoneType' object has no attribute 'group'

只要模式从字符串的起始部分开始匹配,即使字符串模式长,匹配也仍然能够成功:

m = re.match('foo','food on the table')
if m is not None:
    print(m.group())			#foo

充分利用 python 的面向对象的特性,忽略保存中间过程产生的结果:

print(re.match('foo','food on the table').group())		#foo

3.4、使用 search() 在一个字符串中查找模式(搜索与匹配的对比)

search() 的工作方式与 match() 完全一致,不同之处在于 search() 会用它的字符串参数,在任意位置对给正则表达式搜索第一次出现的匹配情况。如果搜索到成功的匹配,就会返回一个匹配对象;否则,返回 None。如:

# re.match('foo','seafood').group()   #报错,匹配失败
m = re.search('foo','seafood')
if m is not None:
    print(m.group())			#foo

3.5、重复、特殊字符以及分组

该表达式允许.com前面有一个或者两个名称:

patt = '\w+@(\w+\.)?\w+\.com'
print(re.match(patt,'nobody@xxx.com').group())		#nobody@xxx.com
print(re.match(patt,'nobody@www.xxx.com').group())		#nobody@www.xxx.com
# print(re.match(patt,'nobody@www.xxx.yyy.com').group()) 	#不匹配

允许任意数量的中间子域名存在,将 “?” 改成了 “*” :

patt = '\w+@(\w+\.)*\w+\.com'
print(re.match(patt,'nobody@wwwsdfg45.xxxdsf.yyy5465.com').group())	#nobody@wwwsdfg45.xxxdsf.yyy5465.com

group() 方法访问每个独立的子组以及 groups() 方法以获取一个包含所有匹配子组的元组:

m = re.match('(\w\w\w)-(\d\d\d)','abx-123')
print(m.group())     #完整匹配,abx-123
print(m.group(1))    #子组1,abx
print(m.group(2))    #子组2,123
print(m.groups())    #全部子组,('abx', '123')

可见,group() 通常用于以普通方式显示所有的匹配部分,但也能用于捕获各个匹配的子组可以使用 groups() 方法来获取一个包含所有匹配子字符串的元组

3.6、匹配字符串的起始和结尾以及单词边界

m = re.search('^The','The end.')
if m is not None:
    print(m.group())		#The

m = re.search(r'\bthe','bite the dog')      #在边界,可以匹配
if m is not None:
    print(m.group())		#the

m = re.search(r'\bthe','bitethe dog')      #不能匹配
if m is not None:
    print(m.group())

m = re.search(r'\Bthe','bitethe dog')      #没有边界,可以匹配
if m is not None:
    print(m.group())		#the

3.7、使用 findall() 和 finditer() 查找每一次出现的位置

findall() 查询字符串中某个正则表达式模式全部的非重复出现的情况。findall() 总是返回一个列表。

print(re.findall('car','carry the barcardi to the car'))	#['car', 'car', 'car']

finditer() 在匹配对象中迭代:

s = 'This and that.'
print(re.findall(r'(th\w+) and (th\w+)',s,re.I))		#[('This', 'that')]
print(re.finditer(r'(th\w+) and (th\w+)',s,re.I).__next__().groups())		#('This', 'that')
print(re.finditer(r'(th\w+) and (th\w+)',s,re.I).__next__().group(1))		#This
print(re.finditer(r'(th\w+) and (th\w+)',s,re.I).__next__().group(2))		#that
print([g.groups() for g in re.finditer(r'(th\w+) and (th\w+)',s,re.I)])		#[('This', 'that')]

在单个字符串中执行单个分组的多重匹配:

print(re.findall(r'(th\w+)',s,re.I))	#['This', 'that']
it = re.finditer(r'(th\w+)',s,re.I)
g = it.__next__()			
print(g.groups())			#('This',)
print(g.group(1))			#This
g = it.__next__()
print(g.groups())			#('that',)
print(g.group(1))			#that
print([g.group(1) for g in re.finditer(r'(th\w+)',s,re.I)])		#['This', 'that']

3.8、使用 sub() 和 subn() 搜索与替换

print(re.sub('X','Mr.Smith','attn: Dear X'))	#attn: Dear Mr.Smith
print(re.subn('X','Mr.Smith','attn: Dear X'))		#('attn: Dear Mr.Smith', 1)
print(re.sub('[ae]','X','abcdef'))		#XbcdXf
print(re.subn('[ae]','X','abcdef'))	#('XbcdXf', 2)

3.9、在限定模式上使用 split() 分隔字符串

print(re.split(':','str1:str2:str3'))	#['str1', 'str2', 'str3']

3.10、拓展符号

3.10.1、(?iLmsux)

通过使用(?iLmsux)系列选项,用户可以直接在正则表达式里面指定一个或者多个标记,而不是通过 complie() 或者其他 re 模块函数

print(re.findall(r'(?i)yes','yes? Yes. YES'))	#['yes', 'Yes', 'YES']
#(?i)即re.I;忽略大小写
print(re.findall(r'(?im)(th[\w ]+)','''
                                     This line is first,
                                     another line,
                                     that line,it's the best'''))		
#['This line is first', 'ther line', 'that line', 'the best']
#(?im),即re.M,多行混合
print(re.findall(r'th.+','''
                            The first line
                            the second line
                            the third line'''))		#['the second line', 'the third line']
print(re.findall(r'(?s)th.+','''
                            The first line
                            the second line
                            the third line'''))		#['the second line\nthe third line']
#(?s),即re.s,表明句点能够用来表示\n符号(通常用来表示除\n之外的全部字符)
print(re.search(r'''(?x)
                \((\d{3})\)
                [ ]
                (\d{3})
                -
                (\d{4})
                ''','(800) 555-1212').groups())		#('800', '555', '1212')
#(?x),即re.x,该标记允许用户通过抑制在正则表达式中使用空白符(除了在字符类中或者在反斜杠转义中)来创建更易读的正则表达式。
print(re.findall(r'http://(?:\w+\.)*(\w+\.com)','http://google.com http://www.google.com http://code.google.com'))
#['google.com', 'google.com', 'google.com']
#(?:...),即可以对部分正则表达式进行分组,但是不会保存该分组用于后续的检索或者应用。当不想保存今后永远不会使用的多余匹配时,这个符号非常有用
print(re.search(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?:\d{4})','(800) 555-1212').groupdict())
#{'areacode': '800', 'prefix': '555'}
#可以同时使用(?P<name>)和(?P=name)符号。前者通过使用一个名称标识符而不是使用从 1 开始增加到 N 的增量数字来保存匹配。如果使用数字来保存,可以使用\1,\2...\N来检索。有标识符的话,可以使用\g<name>检索
print("\t\t\t",re.sub(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?:\d{4})','(\g<areacode>) \g<prefix>-xxxx','(800) 555-1212'))		#(800) 555-xxxx
print(re.findall(r'\w+(?= van Rossum)',
                          '''
                            Guido van Rossum
                            Tim Peters
                            Alex Matelli
                            Just van Rossum
                            Raymond Hettinger
                          '''))     #['Guido', 'Just']
#可以使用(?=...)和(?!...)符号在目标字符串中实现一个前视匹配,而不必实际上使用这些字符串。前者是正向前视断言,后者是负向前视断言。
print(re.findall(r'(?m)^\s+(?!noreply|postmaster)(\w+)',
                          '''
                             sales@phptr.com
                             postmaster@phptr.com
                             eng@phptr.com
                             noreply@phptr.com
                             admin@phptr.com
                          '''))     #['sales', 'eng', 'admin']
print(re.findall(r'(?m)^\s+(?!noreply|postmaster)(\w+)(@\w+\.com)',
                          '''
                             sales@phptr.com
                             postmaster@phptr.com
                             eng@phptr.com
                             noreply@phptr.com
                             admin@phptr.com
                          '''))     
#[('sales', '@phptr.com'), ('eng', '@phptr.com'), ('admin', '@phptr.com')]
print(bool(re.search(r'(?:(x)|y)(?(1)y|x)','xy')))		#True
print(bool(re.search(r'(?:(x)|y)(?(1)y|x)','yx')))		#True
print(bool(re.search(r'(?:(x)|y)(?(1)y|x)','x')))		#False
#使用条件正则表达式。上述匹配一个字符串:两个字母必须由一个字母跟着另一个字母,两个字母不能相同

4、贪婪

在这里插入图片描述

data = 'Thu Aug 12 21:41:08 2010::dvnbue@akffbwvlfvl.gov::1281620468-6-11'

patt = '.+\d+-\d+-\d+'
m = re.search(patt,data)
print("\t\t\t\t",m.group())     #Thu Aug 12 21:41:08 2010::dvnbue@akffbwvlfvl.gov::1281620468-6-11

#+是一个贪婪的操作符
patt = '.+(\d+-\d+-\d+)'
m = re.search(patt,data)
print("\t\t\t\t",m.group(1))     #8-6-11
#Thu Aug 12 21:41:08 2010::dvnbue@akffbwvlfvl.gov::128162046	为 .+匹配的
#8-6-11		为\d+-\d+-\d+匹配的

在这里插入图片描述

#?是一个非贪婪的操作符
patt = '.+?(\d+-\d+-\d+)'
m = re.search(patt,data)
print("\t\t\t\t",m.group(1))     #1281620468-6-11
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值