python正则表达式模块.docx
- 文档编号:7452445
- 上传时间:2023-01-24
- 格式:DOCX
- 页数:23
- 大小:37.05KB
python正则表达式模块.docx
《python正则表达式模块.docx》由会员分享,可在线阅读,更多相关《python正则表达式模块.docx(23页珍藏版)》请在冰豆网上搜索。
python正则表达式模块
原文作者:
A.M.Kuchling(amk@amk.ca)
授权许可:
创作共用协议
翻译人员:
FireHare
校对人员:
Leal
适用版本:
Python1.5及后续版本
摘要
本文是通过Python的re模块来使用正则表达式的一个入门教程,和库参考手册的对应章节相比,更为浅显易懂、循序渐进。
python正则表达式模块简介
Python自1.5版本起增加了re模块,它提供Perl风格的正则表达式模式。
Python1.5之前版本则是通过regex模块提供Emacs风格的模式。
Emacs风格模式可读性稍差些,而且功能也不强,因此编写新代码时尽量不要再使用regex模块,当然偶尔你还是可能在老代码里发现其踪影。
就其本质而言,正则表达式(或RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过re模块实现。
使用这个小型语言,你可以为想要匹配的相应字符串集指定规则;该字符串集可能包含英文语句、e-mail地址、TeX命令或任何你想搞定的东西。
然后你可以问诸如“这个字符串匹配该模式吗?
”或“在这个字符串中是否有部分匹配该模式呢?
”。
你也可以使用RE以各种方式来修改或分割字符串。
正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行。
在高级用法中,也许还要仔细留意引擎是如何执行给定RE,如何以特定方式编写RE以令生产的字节码运行速度更快。
本文并不涉及优化,因为那要求你已充分掌握了匹配引擎的内部机制。
正则表达式语言相对小型和受限(功能有限),因此并非所有字符串处理都能用正则表达式完成。
当然也有些任务可以用正则表达式完成,不过最终表达式会变得异常复杂。
碰到这些情形时,编写Python代码进行处理可能反而更好;尽管Python代码比一个精巧的正则表达式要慢些,但它更易理解。
简单模式
我们将从最简单的正则表达式学习开始。
由于正则表达式常用于字符串操作,那我们就从最常见的任务:
字符匹配下手。
有关正则表达式底层的计算机科学上的详细解释(确定性和非确定性有限自动机),你可以查阅编写编译器相关的任何教科书。
字符匹配
大多数字母和字符一般都会和自身匹配。
例如,正则表达式test会和字符串“test”完全匹配。
(你也可以使用大小写不敏感模式,它还能让这个RE匹配“Test”或“TEST”;稍后会有更多解释。
)
这个规则当然会有例外;有些字符比较特殊,它们和自身并不匹配,而是会表明应和一些特殊的东西匹配,或者它们会影响到RE其它部分的重复次数。
本文很大篇幅专门讨论了各种元字符及其作用。
这里有一个元字符的完整列表;其含义会在本指南余下部分进行讨论。
.^$*+?
{[]\|()
我们首先考察的元字符是"["和"]"。
它们常用来指定一个字符类别,所谓字符类别就是你想匹配的一个字符集。
字符可以单个列出,也可以用“-”号分隔的两个给定字符来表示一个字符区间。
例如,[abc]将匹配"a","b",或"c"中的任意一个字符;也可以用区间[a-c]来表示同一字符集,和前者效果一致。
如果你只想匹配小写字母,那么RE应写成[a-z].
元字符在类别里并不起作用。
例如,[akm$]将匹配字符"a","k","m",或"$"中的任意一个;"$"通常用作元字符,但在字符类别里,其特性被除去,恢复成普通字符。
你可以用补集来匹配不在区间范围内的字符。
其做法是把"^"作为类别的首个字符;其它地方的"^"只会简单匹配"^"字符本身。
例如,[^5]将匹配除"5"之外的任意字符。
也许最重要的元字符是反斜杠"\"。
做为Python中的字符串字母,反斜杠后面可以加不同的字符以表示不同特殊意义。
它也可以用于取消所有的元字符,这样你就可以在模式中匹配它们了。
举个例子,如果你需要匹配字符"["或"\",你可以在它们之前用反斜杠来取消它们的特殊意义:
[或\。
一些用"\"开始的特殊字符所表示的预定义字符集通常是很有用的,象数字集,字母集,或其它非空字符集。
下列是可用的预设特殊字符:
∙\d匹配任何十进制数;它相当于类[0-9]。
∙\D匹配任何非数字字符;它相当于类[^0-9]。
∙\s匹配任何空白字符;它相当于类[\t\n\r\f\v]。
∙\S匹配任何非空白字符;它相当于类[^\t\n\r\f\v]。
∙\w匹配任何字母数字字符;它相当于类[a-zA-Z0-9_]。
∙\W匹配任何非字母数字字符;它相当于类[^a-zA-Z0-9_]。
这样特殊字符都可以包含在一个字符类中。
如,[\s,.]字符类将匹配任何空白字符或","或"."。
本节最后一个元字符是.。
它匹配除了换行字符外的任何字符,在alternate模式(re.DOTALL)下它甚至可以匹配换行。
"."通常被用于你想匹配“任何字符”的地方。
正则表达式重复
正则表达式第一件能做的事是能够匹配不定长的字符集,而这是其它能作用在字符串上的方法所不能做到的。
不过,如果那是正则表达式唯一的附加功能的话,那么它们也就不那么优秀了。
它们的另一个功能就是你可以指定正则表达式的一部分的重复次数。
我们讨论的第一个重复功能的元字符是。
并不匹配字母字符"*";相反,它指定前一个字符可以被匹配零次或更多次,而不是只有一次。
举个例子,ca*t将匹配"ct"(0个"a"字符),"cat"(1个"a"),"caaat"(3个"a"字符)等等。
RE引擎有各种来自C的整数类型大小的内部限制,以防止它匹配超过20亿个"a"字符;你也许没有足够的内存去建造那么大的字符串,所以将不会累计到那个限制。
象*这样地重复是“贪婪的”;当重复一个RE时,匹配引擎会试着重复尽可能多的次数。
如果模式的后面部分没有被匹配,匹配引擎将退回并再次尝试更小的重复。
一步步的示例可以使它更加清晰。
让我们考虑表达式a[bcd]*b。
它匹配字母"a",零个或更多个来自类[bcd]中的字母,最后以"b"结尾。
现在想一想该RE对字符串"abcbd"的匹配。
StepMatchedExplanation
1aa匹配模式
2abcbd引擎匹配[bcd]*,并尽其所能匹配到字符串的结尾
3Failure引擎尝试匹配b,但当前位置已经是字符的最后了,所以失败
4abcb退回,[bcd]*尝试少匹配一个字符。
5Failure再次尝次b,但在当前最后一位字符是"d"。
6abc再次退回,[bcd]*只匹配"bc"。
7abcb再次尝试b,这次当前位上的字符正好是"b"
RE的结尾部分现在可以到达了,它匹配"abcb"。
这证明了匹配引擎一开始会尽其所能进行匹配,如果没有匹配然后就逐步退回并反复尝试RE剩下来的部分。
直到它退回尝试匹配[bcd]到零次为止,如果随后还是失败,那么引擎就会认为该字符串根本无法匹配RE。
另一个重复元字符是+,表示匹配一或更多次。
请注意*和+之间的不同;*匹配零或更多次,所以可以根本就不出现,而+则要求至少出现一次。
用同一个例子,ca+t就可以匹配"cat"(1个"a"),"caaat"(3个"a"),但不能匹配"ct"。
还有更多的限定符。
问号?
匹配一次或零次;你可以认为它用于标识某事物是可选的。
例如:
home-?
brew匹配"homebrew"或"home-brew"。
最复杂的重复限定符是{m,n},其中m和n是十进制整数。
该限定符的意思是至少有m个重复,至多到n个重复。
举个例子,a/{1,3}b将匹配"a/b","a//b"和"a///b"。
它不能匹配"ab"因为没有斜杠,也不能匹配"a////b",因为有四个。
你可以忽略m或n;因为会为缺失的值假设一个合理的值。
忽略m会认为下边界是0,而忽略n的结果将是上边界为无穷大--实际上是先前我们提到的20亿,但这也许同无穷大一样。
细心的读者也许注意到其他三个限定符都可以用这样方式来表示。
{0,}等同于,{1,}等同于+,而{0,1}则与?
相同。
如果可以的话,最好使用,+,或?
。
很简单因为它们更短也更容易懂。
使用正则表达式
现在我们已经看了一些简单的正则表达式,那么我们实际在Python中是如何使用它们的呢?
re模块提供了一个正则表达式引擎的接口,可以让你将REs编译成对象并用它们来进行匹配。
编译正则表达式
正则表达式被编译成RegexObject实例,可以为不同的操作提供方法,如模式匹配搜索或字符串替换。
#!
python
>>>importre
>>>p=pile('ab*')
>>>printp
pile()也接受可选的标志参数,常用来实现不同的特殊功能和语法变更。
我们稍后将查看所有可用的设置,但现在只举一个例子:
#!
python
>>>p=pile('ab*',re.IGNORECASE)
RE被做为一个字符串发送给pile()。
REs被处理成字符串是因为正则表达式不是Python语言的核心部分,也没有为它创建特定的语法。
(应用程序根本就不需要REs,因此没必要包含它们去使语言说明变得臃肿不堪。
)而re模块则只是以一个C扩展模块的形式来被Python包含,就象socket或zlib模块一样
将REs作为字符串以保证Python语言的简洁,但这样带来的一个麻烦就是象下节标题所讲的。
反斜杠的麻烦
在早期规定中,正则表达式用反斜杠字符("\")来表示特殊格式或允许使用特殊字符而不调用它的特殊用法。
这就与Python在字符串中的那些起相同作用的相同字符产生了冲突。
让我们举例说明,你想写一个RE以匹配字符串"\section",可能是在一个LATEX文件查找。
为了要在程序代码中判断,首先要写出想要匹配的字符串。
接下来你需要在所有反斜杠和其它元字符前加反斜杠来取消其特殊意义,结果要匹配的字符串就成了"\section"。
当把这个字符串传递给pile()时必须还是"\section"。
然而,作为Python的字符串实值(stringliterals)来表示的话,"\section"中两个反斜杠还要再次取消特殊意义,最后结果就变成了"\\section"。
字符阶段
\section要匹配的字符串
\\section为pile取消反斜杠的特殊意义
"\\\\section"为"\\section"的字符串实值(stringliterals)取消反斜杠的特殊意义
简单地说,为了匹配一个反斜杠,不得不在RE字符串中写'\\',因为正则表达式中必须是"\",而每个反斜杠在常规的Python字符串实值中必须表示成"\"。
在REs中反斜杠的这个重复特性会导致大量重复的反斜杠,而且所生成的字符串也很难懂。
解决的办法就是为正则表达式使用Python的raw字符串表示;在字符串前加个"r"反斜杠就不会被任何特殊方式处理,所以r"\n"就是包含"\"和"n"的两个字符,而"\n"则是一个字符,表示一个换行。
正则表达式通常在Python代码中都是用这种raw字符串表示。
常规字符串Raw字符串
"ab*"r"ab*"
"\\\\section"r"\\section"
"\\w+\\s+\\1"r"\w+\s+\1"
执行匹配
一旦你有了已经编译了的正则表达式的对象,你要用它做什么呢?
RegexObject实例有一些方法和属性。
这里只显示了最重要的几个,如果要看完整的列表请查阅PythonLibraryReference
方法/属性作用
match()决定RE是否在字符串刚开始的位置匹配
search()扫描字符串,找到这个RE匹配的位置
findall()找到RE匹配的所有子串,并把它们作为一个列表返回
finditer()找到RE匹配的所有子串,并把它们作为一个迭代器返回
如果没有匹配到的话,match()和search()将返回None。
如果成功的话,就会返回一个MatchObject实例,其中有这次匹配的信息:
它是从哪里开始和结束,它所匹配的子串等等。
你可以用采用人机对话并用re模块实验的方式来学习它。
如果你有Tkinter的话,你也许可以考虑参考一下Tools/scripts/redemo.py,一个包含在Python发行版里的示范程序。
首先,运行Python解释器,导入re模块并编译一个RE:
#!
python
Python2.2.2(#1,Feb102003,12:
57:
01)
>>>importre
>>>p=pile('[a-z]+')
>>>p
<_sre.SRE_Patternobjectat80c3c28>
现在,你可以试着用RE的[a-z]+去匹配不同的字符串。
一个空字符串将根本不能匹配,因为+的意思是“一个或更多的重复次数”。
在这种情况下match()将返回None,因为它使解释器没有输出。
你可以明确地打印出match()的结果来弄清这一点。
#!
python
>>>p.match("")
>>>printp.match("")
None
现在,让我们试着用它来匹配一个字符串,如"tempo"。
这时,match()将返回一个MatchObject。
因此你可以将结果保存在变量里以便后面使用。
#!
python
>>>m=p.match('tempo')
>>>printm
<_sre.SRE_Matchobjectat80c4f68>
现在你可以查询MatchObject关于匹配字符串的相关信息了。
MatchObject实例也有几个方法和属性;最重要的那些如下所示:
方法/属性作用
group()返回被RE匹配的字符串
start()返回匹配开始的位置
end()返回匹配结束的位置
span()返回一个元组包含匹配(开始,结束)的位置
试试这些方法不久就会清楚它们的作用了:
#!
python
>>>m.group()
'tempo'
>>>m.start(),m.end()
(0,5)
>>>m.span()
(0,5)
group()返回RE匹配的子串。
start()和end()返回匹配开始和结束时的索引。
span()则用单个元组把开始和结束时的索引一起返回。
因为匹配方法检查到如果RE在字符串开始处开始匹配,那么start()将总是为零。
然而,RegexObject实例的search方法扫描下面的字符串的话,在这种情况下,匹配开始的位置就也许不是零了。
#!
python
>>>printp.match(':
:
:
message')
None
>>>m=p.search(':
:
:
message');printm
>>>m.group()
'message'
>>>m.span()
(4,11)
在实际程序中,最常见的作法是将MatchObject保存在一个变量里,然后检查它是否为None,通常如下所示:
#!
python
p=pile(...)
m=p.match('stringgoeshere')
ifm:
print'Matchfound:
',m.group()
else:
print'Nomatch'
两个RegexObject方法返回所有匹配模式的子串。
findall()返回一个匹配字符串行表:
#!
python
>>>p=pile('\d+')
>>>p.findall('12drummersdrumming,11piperspiping,10lordsa-leaping')
['12','11','10']
findall()在它返回结果时不得不创建一个列表。
在Python2.2中,也可以用finditer()方法。
#!
python
>>>iterator=p.finditer('12drummersdrumming,11...10...')
>>>iterator
>>>formatchiniterator:
...printmatch.span()
...
(0,2)
(22,24)
(29,31)
re模块级函数
你不一定要产生一个RegexObject对象然后再调用它的方法;re模块也提供了顶级函数调用如match()、search()、sub()等等。
这些函数使用RE字符串作为第一个参数,而后面的参数则与相应RegexObject的方法参数相同,返回则要么是None要么就是一个MatchObject的实例。
#!
python
>>>printre.match(r'From\s+','Fromageamk')
None
>>>re.match(r'From\s+','FromamkThuMay1419:
12:
101998')
Underthehood,这些函数简单地产生一个RegexOject并在其上调用相应的方法。
它们也在缓存里保存编译后的对象,因此在将来调用用到相同RE时就会更快。
你将使用这些模块级函数,还是先得到一个RegexObject再调用它的方法呢?
如何选择依赖于怎样用RE更有效率以及你个人编码风格。
如果一个RE在代码中只做用一次的话,那么模块级函数也许更方便。
如果程序包含很多的正则表达式,或在多处复用同一个的话,那么将全部定义放在一起,在一段代码中提前编译所有的REs更有用。
从标准库中看一个例子,这是从xmllib.py文件中提取出来的:
#!
python
ref=pile(...)
entityref=pile(...)
charref=pile(...)
starttagopen=pile(...)
我通常更喜欢使用编译对象,甚至它只用一次,但很少人会像我这样做(如同一个纯粹主义者)。
编译标志
编译标志让你可以修改正则表达式的一些运行方式。
在re模块中标志可以使用两个名字,一个是全名如IGNORECASE,一个是缩写,一字母形式如I。
(如果你熟悉Perl的模式修改,一字母形式使用同样的字母;例如re.VERBOSE的缩写形式是re.X。
)多个标志可以通过按位OR-ing它们来指定。
如re.I|re.M被设置成I和M标志:
这有个可用标志表,对每个标志后面都有详细的说明。
标志含义
DOTALL,S使.匹配包括换行在内的所有字符
IGNORECASE,I使匹配对大小写不敏感
LOCALE,L做本地化识别(locale-aware)匹配
MULTILINE,M多行匹配,影响^和$
VERBOSE,X能够使用REs的verbose状态,使之被组织得更清晰易懂
I
IGNORECASE
使匹配对大小写不敏感;字符类和字符串匹配字母时忽略大小写。
举个例子,[A-Z]也可以匹配小写字母,Spam可以匹配"Spam","spam",或"spAM"。
这个小写字母并不考虑当前位置。
L:
LOCALE
影响\w,\W,\b,和\B,这取决于当前的本地化设置。
locales是C语言库中的一项功能,是用来为需要考虑不同语言的编程提供帮助的。
举个例子,如果你正在处理法文文本,你想用\w+来匹配文字,但\w只匹配字符类[A-Za-z];它并不能匹配"é"或"ç"。
如果你的系统配置适当且本地化设置为法语,那么内部的C函数将告诉程序"é"也应该被认为是一个字母。
当在编译正则表达式时使用LOCALE标志会得到用这些C函数来处理\w后的编译对象;这会更慢,但也会象你希望的那样可以用\w+来匹配法文文本。
M:
MULTILINE
(此时^和$不会被解释;它们将在4.1节被介绍.)
使用"^"只匹配字符串的开始,而$则只匹配字符串的结尾和直接在换行前(如果有的话)的字符串结尾。
当本标志指定后,"^"匹配字符串的开始和字符串中每行的开始。
同样的,$元字符匹配字符串结尾和字符串中每行的结尾(直接在每个换行之前)。
SDOTALL
使"."特殊字符完全匹配任何字符,包括换行;没有这个标志,"."匹配除了换行外的任何字符。
XVERBOSE
该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
当该标志被指定时,在RE字符串中的空白符被忽略,除非该空白符在字符类中或在反斜杠之后;这可以让你更清晰地组织和缩进RE。
它也可以允许你将注释写入RE,这些注释会被引擎忽略;注释用"#"号来标识,不过该符号不能在字符串或反斜杠之后。
举个例子,这里有一个使用re.VERBOSE的RE;看看读它轻松了多少?
#!
python
charref=pile(r"""&[[]]#Startofanumericentityreference|||herehaswrong.ican'tfix
(
[0-9]+[^0-9]#Decimalform
|0[0-7]+[^0-7]#Octalform
|x[0-9a-fA-F]+[^0-9a-fA-F]#Hexadecimalform
)
""",re.VERBOSE)
没有verbose设置,RE会看起来象这样:
#!
python
charref=pile("([0-9]+[^0-9]"
"|0[0-7]+[^0-7]"
"|x[0-9a-fA-F]+[^0-9a-fA-F])")
在上面的例子里,Python的字符串自动连接可以用来将RE分成更小的部分,但它比用re.VERBOSE标志时更难懂
更多模式功能
到目前为止,我们只展示了正则表达式的一部分功能。
在本节,我们将展示一些新的元字符和如何使用组来检索被匹配的文本部分。
ddddddddddddddddddddddddddddd
更多的元字符
粗体文字链接标题还有一些我们还没展示的元字符,其中的大部分将在本节展示。
剩下来要讨论的一部分元字符是零宽界定符(zero-w
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- python 正则 表达式 模块