收藏 分销(赏)

Python正则表达式操作指南.pdf

上传人:曲**** 文档编号:225506 上传时间:2023-03-08 格式:PDF 页数:27 大小:1.06MB
下载 相关 举报
Python正则表达式操作指南.pdf_第1页
第1页 / 共27页
Python正则表达式操作指南.pdf_第2页
第2页 / 共27页
点击查看更多>>
资源描述
Python正则表达式操作指南出自Ubuntu中文原文出处:http:www.amk.ca/python/howto/regex/原文作者:A.M.Kuchling(amkamk.ca)授权许可:创作共享协议翻译人员:FireHare校对人员:校al适用版本:Python 1.5及后续版本摘要本文是通过Python的re模块来使用正则表达式的一个入门教程,和库参考手册的对应章节 相比,更为浅显易懂、循序渐进。本文可以从 http:www.amk.ca/python/howto 捕获目录目录隐藏11简介2简单模式2字符匹配2.2重复3使用正则表达式3.1 编译正则表达式3.2 反斜杠的麻烦3.3 执行匹配3.4 模块级函数3.5 编译标志4更多模式功能4.1 更多的元字符4.2 分组4.3 无捕获组和命名组4.4 前向界定符5修改字符串5.1 将字符串分片5.2 搜索和替换6常见问题6.1 使用字符串方式6.2 match。vs search。6.3 贪婪vs不贪婪6.4 不用 re.VERBOSE7反馈8关于本文档简介Python自1.5版本起增加了re模块,它提供Perl风格的正则表达式模式。Python 1.5之前版 本则是通过regex模块提供Emecs风格的模式。Emacs风格模式可读性稍差些,而且功能 也不强,因此编写新代码时尽量不要再使用regex模块,当然偶尔你还是可能在老代码里 发现其踪影。就其本质而言,正则表达式(或RE)是一种小型的、高度专业化的编程语言,(在Python 中)它内嵌在Python中,并通过re模块实现。使用这个小型语言,你可以为想要匹配的相 应字符串集指定规则;该字符串集可能包含英文语句、e-mail地址、TeX命令或任何你想搞 定的东西。然后你可以问诸如“这个字符串匹配该模式吗?或在这个字符串中是否有部分 匹配该模式呢?你也可以使用RE以各种方式来修改或分割字符串。正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行。在高级用法 中,也许还要仔细留意引擎是如何执行给定RE,如何以特定方式编写RE以令生产的字 节码运行速度更快。本文并不涉及优化,因为那要求你已充分掌握了匹配引擎的内部机制。正则表达式语言相对小型和受限(功能有限),因此并非所有字符串处理都能用正则表达式 完成。当然也有些任务可以用正则表达式完成,不过最终表达式会变得异常复杂。碰到这 些情形时,编写Python代码进行处理可能反而更好;尽管Python代码比一个精巧的正则 表达式要慢些,但它更易理解。简单模式我们将从最简单的正则表达式学习开始。山于正则表达式常用于字符串操作,那我们就从最 常见的任务:字符匹配下手。有关正则表达式底层的计算机科学上的详细解释(确定性和非确定性有限自动机),你可以 查阅编写编译器相关的任何教科书。字符匹配大多数字母和字符一般都会和自身匹配。例如,正则表达式test会和字符串“test”完全匹配。(你也可以使用大小写不敏感模式,它还能让这个RE匹配“Test”或“TEST”;稍后会有更 多解释。)这个规则当然会有例外;有些字符比较特殊,它们和自身并不匹配,而是会表明应和一些特 殊的东西匹配,或者它们会影响到RE其它部分的重复次数。本文很大篇幅专门讨论了各 种元字符及其作用。这里有一个元字符的完整列表;其含义会在本指南馀下部分进行讨论。.A$*+?|0我们首先考察的元字符是和%它们常用来指定一个字符类别,所谓字符类别就是你 想匹配的一个字符集。字符可以单个列出,也可以用号分隔的两个给定字符来表示一个 字符区间。例如,abc将匹配或c中的任意一个字符;也可以用区间a-c来表 示同一字符集,和前者效果一致。如果你只想匹配小写字母,那么RE应写成a-z.元字符在类别里并不起作用。例如,akm$将匹配字符或中的任意一个;$通常用作元字符,但在字符类别里,其特性被除去,恢复成普通字符。你可以用补集来匹配不在区间范围内的字符。其做法是把它作为类别的首个字符;其它地 方的“a只会简单匹配字符本身。例如,r5将匹配除5之外的任意字符。也许最重要的元字符是反斜杠叫。做为Python中的字符串字母,反斜杠后面可以加不同 的字符以表示不同特殊意义。它也可以用于取消所有的元字符,这样你就可以在模式中匹配 它们了。举个例子,如果你需要匹配字符T或叫,你可以在它们之前用反斜杠来取消 它们的特殊意义:或Wo一些用开始的特殊字符所表示的预定义字符集通常是很有用的,象数字集,字母集,或其它非空字符集。下列是可用的预设特殊字符:d匹配任何十进制数;它相当于类0-9oD匹配任何非数字字符;它相当于类0-9。s匹配任何空白字符;它相当于类tnrfvoS匹配任何非空白字符;它相当于类Ftnrfv。w匹配任何字母数字字符;它相当于类a-zA-Z0-9_oW匹配任何非字母数字字符;它相当于类Fa-zA-ZO-9。这样特殊字符都可以包含在一个字符类中。如,字符类将匹配任何空白字符或,或本节最后一个元字符是,。它匹配除了换行字符外的任何字符,在alternate模式(re.DOTALL)下它甚至可以匹配换行。通常被用于你想匹配“任何字符”的地方。重复正则表达式第一件能做的事是能够匹配不定长的字符集,而这是其它能作用在字符串上的方 法所不能做到的。不过,如果那是正则表达式唯一的附加功能的话,那么它们也就不那么 优秀了。它们的另一个功能就是你可以指定正则表达式的一部分的重复次数。我们讨论的第一个重复功能的元字符是*。*并不匹配字母字符相反,它指定前一个 字符可以被匹配零次或更多次,而不是只有一次。举个例子,ca*t将匹配ct(0个a字符),cat(1个a),caaat(3个a字符)等等。RE引擎有各种来自C的整数类型大小的内部限制,以防止它匹配超过2亿个a字符;你也许没有足够的内存去建造那么大的字符串,所以将不会累计到那个限制。象*这样地重复是“贪婪的”;当重复一个RE时,匹配引擎会试着重复尽可能多的次数。如果模式的后面部分没有被匹配,匹配引擎将退回并再次尝试更小的重复。一步步的示例可以使它更加清晰。让我们考虑表达式abcd*bo它匹配字母a,零个或更 多个来自类bed中的字母,最后以b结尾。现在想一想该RE对字符串abebd的匹 配。StepMatchedExplanation1aa匹配模式2abebd引擎匹配bed*,并尽其所 能匹配到字符串的结尾3Failure引擎尝试匹配b,但当前位置 已经是字符的最后了,所以失 败4abeb退回,bed*尝试少匹配一个 字符。5Failure再次尝次b,但在当前最后一 位字符是d 6abc再次退回,bed*只匹配 be%7abeb再次尝试b,这次当前位上 的字符正好是bRE的结尾部分现在可以到达了,它匹配abcb。这证明了匹配引擎一开始会尽其所能进行 匹配,如果没有匹配然后就逐步退回并反复尝试RE剩下来的部分。直到它退回尝试匹配 bed到零次为止,如果随后还是失败,那么引擎就会认为该字符串根本无法匹配RE o另一个重复元字符是+,表示匹配一或更多次。请注意*和+之间的不同;*匹配零或 更多次,所以根本就可以不出现,而+则要求至少出现一次。用同一个例子,ca+t就可以 匹配cat(1 个a),caaat(3 个a),但不能匹配cto还有更多的限定符。问号?匹配一次或零次;你可以认为它用于标识某事物是可选的。例 如:home-?brew 匹配homebrew或home-brew o最复杂的重复限定符是m,n,其中m和n是十进制整数。该限定符的意思是至少有m 个重复,至多到n个重复。举个例子,a/l,3b将匹配a/b,a/b和a/bo它不能匹 配ab因为没有斜杠,也不能匹配a/b,因为有四个。你可以忽略m或n;因为会为缺失的值假设一个合理的值。忽略m会认为下边界是0,而忽略n的结果将是上边界为无穷大-实际上是先前我们提到的2兆,但这也许同无穷 大一样。细心的读者也许注意到其他三个限定符都可以用这样方式来表示。0,等同于*,1,等 同于+,而0,1则与?相同。如果可以的话,最好使用*,+,或?。很简单因为它们更短 也再容易懂。使用正则表达式现在我们已经看了一些简单的正则表达式,那么我们实际在Python中是如何使用它们的 呢?re模块提供了一个正则表达式引擎的接口,可以让你将REs编译成对象并用它们来 进行匹配。编译正则表达式正则表达式被编译成RegexObject实例,可以为不同的操作提供方法,如模式匹配搜索或 字符串替换。#!python import re p=pile(ab*)print pile()也接受可选的标志参数,常用来实现不同的特殊功能和语法变更。我们稍后将 查看所有可用的设置,但现在只举一个例子:#!python p=pile(ab*,re.IGNORECASE)RE被做为一个字符串发送给pileQ REs被处理成字符串是因为正则表达式不是 Python语言的核心部分,也没有为它创建特定的语法。(应用程序根本就不需要REs,因此 没必要包含它们去使语言说明变得臃肿不堪。)而re模块则只是以一个C扩展模块的形式 来被Python包含,就象socket或zlib模块一样。将REs作为字符串以保证Python语言的简洁,但这样带来的一个麻烦就是象下节标题所 讲的。反斜杠的麻烦在早期规定中,正则表达式用反斜杠字符()来表示特殊格式或允许使用特殊字符而不调 用它的特殊用法。这就与Python在字符串中的那些起相同作用的相同字符产生了冲突。让我们举例说明,你想写一个RE以匹配字符串section,可能是在一个LATEX文件查 找。为了要在程序代码中判断,首先要写出想要匹配的字符串。接下来你需要在所有反斜杠 和元字符前加反斜杠来取消其特殊意义。字符阶段section要匹配的字符串Wsection为recompile取消反斜杠的特殊意义WWsection为字符串取消反斜杠简单地说,为了匹配一个反斜杠,不得不在RE字符串中写WW,因为正则表达式中必须 是而每个反斜杠按Python字符串字母表示的常规必须表示成%在REs中反斜 杠的这个重复特性会导致大量重复的反斜杠,而且所生成的字符串也很难懂。解决的办法就是为正则表达式使用Python的raw字符串表示;在字符串前加个r”反斜 杠就不会被任何特殊方式处理,所以rn就是包含叫,和n的两个字符,而n则是 一个字符,表示一个换行。正则表达式通常在Python代码中都是用这种raw字符串表示。常规字符串Raw字符串ab*rab*WWsectionrsectionw+s+lrw+s+l执行匹配一旦你有了已经编译了的正则表达式的对象,你要用它做什么呢?RegexObject实例有一 些方法和属性。这里只显示了最重要的几个,如果要看完整的列表请查阅Python Library Reference方法/属性作用match()决定RE是否在字符串刚开始的位置匹配search()扫描字符串,找到这个RE匹配的位置findall()找到RE匹配的所有子串,并把它们作为一 个列表返回finditer()找到RE匹配的所有子串,并把它们作为一 个迭代器返回如果没有匹配到的话,match()和search()将返回None。如果成功的话,就会返回一个 MatchObject实例,其中有这次匹配的信息:它是从哪里开始和结束,它所匹配的子串等 等。你可以用采用人机对话并用re模块实验的方式来学习它。如果你有Tkinter的话,你也许 可以考虑参考一下Too Is/scripts/redemo.py,一个包含在Python发行版里的示范程序。首先,运行Python解释器,导入re模块并编译一个RE:#!pythonPython 2.2.2(#1,Feb 10 2003,12:57:01)import re p=pile(a-z+)p现在,你可以试着用RE的a-z+去匹配不同的字符串。一个空字符串将根本不能匹配,因为+的意思是“一个或更多的重复次数在这种情况下match。将返回None,因为 它使解释器没有输出。你可以明确地打印出match。的结果来弄清这一点。#!python p.match()print p.match()None现在,让我们试着用它来匹配一个字符串,如tempo。这时,match。将返回一个 MatchObjecto因此你可以将结果保存在变量里以便后面使用。#!python m=p.match(tempo)print m现在你可以查询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 print p.match(:message)None m=p.search(:message);print m m.group()message m.span()(4,H)在实际程序中,最常见的作法是将MatchObject保存在一个变量里,然后检查它是否为 None,通常如下所示:#!pythonp=pile(.)m=p.match(string goes here)if m:print Match found:m.group()else:print No match两个RegexObject方法返回所有匹配模式的子串。flndall。返回一个匹配字符串行表:#!python p=pile(d+)p.findall(12 drummers drumming,11 pipers piping,10 lords a-leaping)12,ll,10findall()在它返回结果时不得不创建一个列表。在Python 2.2中,也可以用finditer()方法。#!python iterator=p.finditer(12 drummers drumming,11.10.)iterator for match in iterator:.print match.span()(0,2)(2 2,2 4)(2 9,31)模块级函数你不一定要产生一个RegexObject对象然后再调用它的方法;re模块也提供了顶级函数 调用如match。、search()sub()等等。这些函数使用RE字符串作为第一个参数,而后面 的参数则与相应,RegexObject的方法参数相同,返回则要么是None要么就是一个 MatchObject 的实例。#!python print re.match(rFroms+,Fromage amk)None re.match(rFroms+,From amk Thu May 14 19:12:10 1998)Under the hood,这些函数简单地产生一个RegexOject并在其上调用相应的方法。它们也在 缓存里保存编译后的对象,因此在将来调用用到相同RE时就会更快。你将使用这些模块级函数,还是先得到一个RegexObject再调用它的方法呢?如何选择依 赖于怎样用RE更有效率以及你个人编码风格。如果一个RE在代码中只做用一次的话,那么模块级函数也许更方便。如果程序包含很多的正则表达式,或在多处复用同一个的话,那么将全部定义放在一起,在一段代码中提前编译所有的REs更有用。从标准库中看一 个例子,这是从xmllib.py文件中提取出来的:#!pythonref=pile(.)entityref=pile(.)charref=pile(.)starttagopen=pile(.)我通常更喜欢使用编译对象,甚至它只用一次,but few people will be as much of a purist about this as I am。编译标志编译标志让你可以修改正则表达式的一些运行方式。在re模块中标志可以使用两个名字,一个是全名如IGNORECASE,一个是缩写,一字母形式如L(如果你熟悉Perl的模式修 改,一字母形式使用同样的字母;例如re.VERBOSE的缩写形式是re.Xo)多个标志可以通 过按位OR-ing它们来指定。如re.I|re.M被设置成I和M标志:这有个可用标志表,对每个标志后面都有详细的说明。标志含义DOTALL,S使.匹配包括换行在内的所有字符IGNORECASE,I使匹配对大小写不敏感LOCALE,L做本地化识别(locale-aware)匹配MULTILINE,M多行匹配,影响人和$VERBOSE,X能够使用REs的verbose状态,使之被组 织得更清晰易懂IGNORECASE使匹配对大小写不敏感;字符类和字符串匹配字母时忽略大小写。举个例子,A-Z也可以 匹配小写字母,Spam可以匹配Spam,spam,或spAM0这个小写字母并不考虑当前 位置。LLOCALE影响w,W,b,和B,这取决于当前的本地化设置。locales是C语言库中的一项功能,是用来为需要考虑不同语言的编程提供帮助的。举个例 子,如果你正在处理法文文本,你想用w+来匹配文字,但w只匹配字符类A-Za-z;它并不能匹配监”或 V如果你的系统配置适当且本地化设置为法语,那么内部的C函 数将告诉程序e也应该被认为是一个字母。当在编译正则表达式时使用LOCALE标志 会得到用这些C函数来处理w后的编译对象;这会更慢,但也会象你希望的那样可以用 w+来匹配法文文本。MMULTILINE(此时人和$不会被解释;它们将在4.1节被介绍.)使用A只匹配字符串的开始,而$则只匹配字符串的结尾和直接在换行前(如果有的话)的字符串结尾。当本标志指定后,人匹配字符串的开始和字符串中每行的开始。同样的,$元字符匹配字符串结尾和字符串中每行的结尾(直接在每个换行之前)。SDOTALL使J特殊字符完全匹配任何字符,包括换行;没有这个标志,匹配除了换行外的任 何字符。XVERBOSE该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。当该标志被指定时,在RE字符串中的空白符被忽略,除非该空白符在字符类中或在反斜杠之后;这可以让你 更清晰地组织和缩进REo它也可以允许你将注释写入RE,这些注释会被引擎忽略;注释 用卅号来标识,不过该符号不能在字符串或反斜杠之后。举个例子,这里有一个使用re.VERBOSE的RE;看看读它轻松了多少?#!pythoncharref=pile(r&#Start of a numeric entity reference(0-9+A0-9#Decimal form|00-7+A0-7#Octal form|x0-9a-fA-F+A0-9a-fA-F#Hexadecimal form)re.VERBOSE)没有verbose设置,RE会看起来象这样:#!pythoncharref=pile(&#(0-9+A0-9|00-7+A0-7|x0-9a-fA-F+A0-9a-fA-F)在上面的例子里,Python的字符串自动连接可以用来将RE分成更小的部分,但它比用 re.VERBOSE标志时更难懂。更多模式功能到目前为止,我们只展示了正则表达式的一部分功能。在本节,我们将展示一些新的元字符 和如何使用组来检索被匹配的文本部分。更多的元字符还有一些我们还没展示的元字符,其中的大部分将在本节展示。剩下来要讨论的一部分元字符是零宽界定符(zero-width assertions)0它们并不会使引擎在处 理字符串时更快;相反,它们根本就没有对应任何字符,只是简单的成功或失败.举个例子,b是一个在单词边界定位当前位置的界定符(assertions),这个位置根本就不会被b改变。这意味着零宽界定符(zero-width assertions)将永远不会被重复,因为如果它们在给定位置 匹配一次,那么它们很明显可以被匹配无数次。可选项,或者or操作符。如果A和B是正则表达式,A|B将匹配任何匹配了 A或 B的字符串。|的优先级非常低,是为了当你有多字符串要选择时能适当地运行。Crow|Servo 将匹配Crow或Servo,而不是Cro,一个w或一个S,和ervoo为了匹配字母T,可以用|,或将其包含在字符类中,如|。匹配行首。除非设置MULTILINE标志,它只是匹配字符串的开始。在MULTILINE模式 里,它也可以直接匹配字符串中的每个换行。例如,如果你只希望匹配在行首单词From,那么RE将用人From。#!python print re.search(AFrom,From Here to Eternity)print re.search(AFrom,Reciting From Memory)None$匹配行尾,行尾被定义为要么是字符串尾,要么是一个换行字符后面的任何位置。#!python print re.search($,block)print re.search($,block)None print re.search($,blockn)匹配一个使用$或将其包含在字符类中,如$。A只匹配字符串首。当不在MULTILINE模式,A和人实际上是一样的。然而,在 MULTILINE模式里它们是不同的;A只是匹配字符串首,而人还可以匹配在换行符之后 字符串的任何位置。ZMatches only at the end of the string.只匹配字符串尾。b单词边界。这是个零宽界定符(zero-width assertions)只用以匹配单词的词首和词尾。单词 被定义为一个字母数字序列,因此词尾就是用空白符或非字母数字符来标示的。下面的例子只匹配class整个单词;而当它被包含在其他单词中时不匹配。#!python p=pile(rbclassb)print p.search(no class at all)print p.search(the declassified algorithm)None print p.search(one subclass is)None当用这个特殊序列时你应该记住这里有两个微妙之处。第一个是Python字符串和正则表达 式之间最糟的冲突。在Python字符串里,b是反斜杠字符,ASCII值是8。如果你没有使 用raw字符串时一,那么Python将会把“b”转换成一个回退符,你的RE将无法象你希 望的那样匹配它了。下面的例子看起来和我们前面的RE 一样,但在RE字符串前少了一 个r o#!python p=pile(bclassb)print p.search(no class at all)None print p.search(b+class+b)第二个在字符类中,这个限定符(assertion)不起作用,b表示回退符,以便与Python字 符串兼容.B另一个零宽界定符(zero-width assertions),它正好同b相反,只在当前位置不在单词边界 时匹配。分组你经常需要得到比RE是否匹配还要多的信息。正则表达式常常用来分析字符串,编写一 个RE匹配感兴趣的部分并将其分成几个小组。举个例子,一个RFC-82 2的头部用隔 成一个头部名和一个值,这就可以通过编写一个正则表达式匹配整个头部,用一组匹配头部 名,另一组匹配头部值的方式来处理。组是通过(和元字符来标识的。(和)有很多在数学表达式中相同的意思;它 们一起把在它们里面的表达式组成一组。举个例子,你可以用重复限制符,象*,+,?,和 来重复组里的内容,比如说(ab)*将匹配零或更多个重复的abo#!python p=pile(ab)*)print p.match(ababababab).span()(0,10)组用(和来指定,并且得到它们匹配文本的开始和结尾索引;这就可以通过一个参 数用group。、start()end()和span。来进行检索。组是从0开始计数的。组0总是存在;它就是整个RE,所以MatchObject的方法都把组0作为它们缺省的参数。稍后我们将 看到怎样表达不能得到它们所匹配文本的spano#!python p=pile(a)b)m=p.match(ab)m.group()ab m.group(O)ab小组是从左向右计数的,从1开始。组可以被嵌套。计数的数值可以能过从左到右计算打开 的括号数来确定。#!python p=pile(a(b)c)d)m=p.match(abcd)m.group(O)abed m.group(l)abc m.group(2)bgroup()可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。#!python m.group(2,l,2)(b,abc,b)The groups()方法返回一个包含所有小组字符串的元组,从1到所含的小组号。#!python m.groups()(abc,b)模式中的逆向引用允许你指定先前捕获组的内容,该组也必须在字符串当前位置被找到。举 个例子,如果组1的内容能够在当前位置找到的话,1就成功否则失败。记住Python字 符串也是用反斜杠加数据来允许字符串中包含任意字符的,所以当在RE中使用逆向引用 时确保使用raw字符串。例如,下面的RE在一个字符串中找到成双的词。#!python p=pile(r(bw+)s+1)p.search(Paris in the the spring).group()the the象这样只是搜索一个字符串的逆向引用并不常见-用这种方式重复数据的文本格式并不 多见-但你不久就可以发现它们用在字符串替换上非常有用。无捕获组和命名组精心设计的REs也许会用很多组,既可以捕获感兴趣的子串,又可以分组和结构化RE本 身。在复杂的REs里,追踪组号变得困难。有两个功能可以对这个问题有所帮助。它们也 都使用正则表达式扩展的通用语法,因此我们来看看第一个。Perl 5对标准正则表达式增加了几个附加功能,Python的re模块也支持其中的大部分。选 择一个新的单按键元字符或一个以开始的特殊序列来表示新的功能,而又不会使Perl 正则表达式与标准正则表达式产生混乱是有难度的。如果你选择做为新的元字符,举 个例子,老的表达式认为是一个正常的字符,而不会在使用&或&时也不会转 义。Perl开发人员的解决方法是使用(?.)来做为扩展语法。?在括号后面会直接导致一个语 法错误,因为?没有任何字符可以重复,因此它不会产生任何兼容问题。紧随?之后 的字符指出扩展的用途,因此(?=f。)Python新增了一个扩展语法到Perl扩展语法中。如果在问号后的第一个字符是P,你就 可以知道它是针对Python的扩展。目前有两个这样的扩展:(?P.)定义一个命名 组,(?P=name)则是对命名组的逆向引用。如果Perl 5的未来版本使用不同的语法增加了 相同的功能,那么re模块也将改变以支持新的语法,这是为了兼容性的目的而保持的 Python专用语法。现在我们看一下普通的扩展语法,我们回过头来简化在复杂REs中使用组运行的特性。因 为组是从左到右编号的,而且一个复杂的表达式也许会使用许多组,它可以使跟踪当前组号 变得困难,而修改如此复杂的RE是十分麻烦的。在开始时插入一个新组,你可以改变它 之后的每个组号。首先,有时你想用一个组去收集正则表达式的一部分,但又对组的内容不感兴趣。你可以用 一个无捕获组:(?:)来实现这项功能,这样你可以在括号中发送任何其他正则表达式。#!python m=re.match(abc)+,abc)m.groups()(K)m=re.match(?:abc)+,abc)m.groups()0除了捕获匹配组的内容之外,无捕获组与捕获组表现完全一样;你可以在其中放置任何字符,可以用重复元字符如来重复它,可以在其他组(无捕获组与捕获组)中嵌套它。:)对 于修改已有组尤其有用,因为你可以不用改变所有其他组号的情况下添加一个新组。捕获组 和无捕获组在搜索效率方面也没什么不同,没有哪一个比另一个更快。其次,更重要和强大的是命名组;与用数字指定组不同的是,它可以用名字来指定。命令组的语法是Python专用扩展之一:(?P.)o名字很明显是组的名字。除了该组 有个名字之外,命名组也同捕获组是相同的。MatchObject的方法处理捕获组时接受的要 么是表示组号的整数,要么是包含组名的字符串。命名组也可以是数字,所以你可以通过两 种方式来得到一个组的信息:#!python p=pile(r(?Pbw+b)m=p.search(Lots of punctuation)m.group(word)Lots m.group(l)Lots命名组是便于使用的,因为它可以让你使用容易记住的名字来代替不得不记住的数字。这里 有一个来自imaplib模块的RE示例:#!pythonInternalDate=pile(rINTERNALDATEr(?P 12 30-9)-(?PA-Za-za-z)-r(?P0-90-90-90-9)r(?P0-90-9):(?P0-90-9):(?P0-90-9)r(?P-+)(?P0-90-9)(?P0-90-9)r)很明显,得到m.group(zonem)要比记住得到组9要容易得多。因为逆向引用的语法,象这样的表达式所表示的是组号,这时用组名代替组号自然 会有差别。还有一个Python扩展:(?P=name),它可以使叫name的组内容再次在当前位 置发现。正则表达式为了找到重复的单词,(bw+)s+l也可以被写成(?Pbw+)s+(?P=word):#!python p=pile(r(?Pbw+)s+(?P=word),)p.search(Paris in the the spring).group()the the前向界定符另一个零宽界定符(zero-width assertion)是前向界定符。前向界定符包括前向肯定界定符 和后向肯定界定符,所下所示:(?=)前向肯定界定符。如果所含正则表达式,以表示,在当前位置成功匹配时成功,否则失 败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩馀部分还要尝试界定符 的右边。(?!)前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功通过示范在哪前向可以成功有助于具体实现。考虑一个简单的模式用于匹配一个文件名,并 将其通过分成基本名和扩展名两部分。如在news.rc中,news是基本名,rc是 文件的扩展名。匹配模式非常简单:.*.*$注意需要特殊对待,因为它是一个元字符;我把它放在一个字符类中。另外注意后面 的$;添加这个是为了确保字符串所有的剩馀部分必须被包含在扩展名中。这个正则表达式 匹配fbo上ar、autoexec上at、sendmail.cf和printers.confo现在,考虑把问题变得复杂点;如果你想匹配的扩展名不是bat的文件名?一些不正确的 尝试:.*.Ab.*$上面的第一次去除bat的尝试是要求扩展名的第一个字符不是bo这是错误的,因为该 模式也不能匹配foo.bar。.*-(Ab.|.Aa.|.rt)$当你试着修补第一个解决方法而要求匹配下列情况之一时表达式更乱了:扩展名的第一个字 符不是b;第二个字符不是a;或第三个字符不是to这样可以接受foo.bar而拒绝 autoexec.bat,但这要求只能是二个字符的扩展名而不接受两个字符的扩展名如 sendmail.cfo我们将在努力修补它时再次把该模式变得复杂。.*.(Ab.?.?|.Aa?.?|.?At?)$在第三次尝试中,第二和第三个字母都变成可选,为的是允许匹配比三个字符更短的扩展名,如sendmail.cf。该模式现在变得非常复杂,这使它很难读懂。更糟的是,如果问题变化了,你想扩展名不是 bat和exe,该模式甚至会变得更复杂和混乱。前向否定把所有这些裁剪成:.*.(?!bat$),*$前向的意思:如果表达式bat在这里没有匹配,尝试模式的其馀部分;如果bat$匹配,整 个模式将失败。后面的$被要求是为了确保象sample.batch这样扩展名以bat开头的 会被允许。将另一个文件扩展名排除在外现在也容易;简单地将其做为可选项放在界定符中。下面的这 个模式将以bat或exe结尾的文件名排除在外。.*.(?!bat$|exe$).*$修改字符串到目前为止,我们简单地搜索了一个静态字符串。正则表达式通常也用不同的方式,通过下 面的RegexObject方法,来修改字符串。方法/属性作用split()将字符串在RE匹配的地方分片并生成一 个列表,sub()找到RE匹配的所有子串,并将其用一个不 同的字符串替换subn()与sub()相同,但返回新的字符串和替换次 数将字符串分片RegexObject的split()方法在RE匹配的地方将字符串分片,将返回列表。它同字符串 的split()方法相似但提供更多的定界符;split。只支持空白符和固定字符串。就象你预料的 那样,也有一个模块级的re.split()函数。split(string,maxsplit=0)通过正则表达式将字符串分片。如果捕获括号在RE中使用,那么它们的内容也会作为结 果列表的一部分返回。如果maxsplit非零,那么最多只能分出maxsplit个分片。你可以通过设置maxsplit值来限制分片数。当maxsplit非零时,最多只能有maxsplit个 分片,字符串的其馀部分被做为列表的最后部分返回。在下面的例子中,定界符可以是非数 字字母字符的任意序列。#!python p=pile(rW+)p.split(This is a test,short and sweet,of split().)This,is,a,test,short,and,sweet,of,split,p.split(This is a test,short and sweet,of split().,3)This,is,a,test,short and sweet,of split().有时,你不仅对定界符之间的文本感兴趣,也需要知道定界符是什么。如果捕获括号在RE 中使用,那么它们的值也会当作列表的一部分返回I。比较下面的调用:#!python p=pile(rW+)p2=pile(r(W+),)p.split(This.is a test.)This,is,a,test,p2.split(This.is a test.)This,is,a,test,模块级函数re.split()将RE作为第一个参数,其他一样。#!python rc.split(W+,Words,words,words.)Words,words,words,re.split(W+),Words,words,words.)Words,words,words,re.split(W+,Words,words,words.,1)Words,words,words.搜索和替换其他常见的用途就是找到所有模式匹配的字符串并用不同的字符串来替换它们。sub()方法 提供一个替换值,可以是字符串或一个函数,和一个要被处理的字符串。sub(replacement,string,count=01)返回的字符串是在字符串中用RE最左边不重复的匹配来替换。如果模式没有发现,字符 将被没有改变地返回。可选参数count是模式匹配后替换的最大次数;count必须是非负整数。缺省值是0表示 替换所有的匹配。这里有个使用sub()方法的简单例子。它用单词colour替换颜色名。#!pytho
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 通信科技 > 开发语言

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服