python正则表达式深入浅出_第1页
python正则表达式深入浅出_第2页
python正则表达式深入浅出_第3页
python正则表达式深入浅出_第4页
python正则表达式深入浅出_第5页
免费预览已结束,剩余11页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、python 的正则表达式 re延伸阅读:python 的内建函数和 subprocess。此文是本系列的第三篇文章了,和之前一样,内容出自官方文档,但是会有自己的理解,并非单纯的翻译。所以,如果我理解有误,欢迎指正,谢谢。本模块提供了和 Perl 里的正则表达式类似的功能,不关是正则表达式本身还是被搜索的字符串,都可以是 Unicode 字符,这点不用担心,python 会处理地和 Ascii 字符一样漂亮。正则表达式使用反斜杆()来转义特殊字符,使其可以匹配字符本身,而不是指定其他特殊的含义。这可能会和 python 字面意义上的字符串转义相冲突,这也许有些令人费解。比如,要匹配一个反斜杆

2、本身,你也许要用来做为正则表达式的字符串,因为正则表达式要是,而字符串里,每个反斜杆都要写成。你也可以在字符串前加上 r 这个前缀来避免部分疑惑, 因为 r 开头的 python 字符串是 raw 字符串,所以里面的所有字符都不会被转义,比如 rn,这个字符串就是一个反斜杆加上一字母 n,而n我们知道这是个换行符。因此,上面的你也可以写成 r,这样,应该就好理解很多了。可以看下面这段:importres=x5c#0 x5c 就是反斜杆printsre.match(,s)#这样可以匹配 re.match(r,s)#这样也可以 _sre.SRE_Matchobjectat0 x80ce2c0 re

3、.match(,s)#但是这样不行Traceback(mostrecentcalllast):File,line1,inFile/usr/lib/python2.6/re.py,line137,inmatchreturn_compile(pattern,flags).match(string)File/usr/lib/python2.6/re.py,line245,in_compileraiseerror,v#invalidexpressionsre_constants.error:bogusescape(endofline)另外值得一提的是,re 模块的方法,大多也就是 RegexObjec

4、t 对象的方法,两者的区别在于执行效率。这个在最后再展开吧正则表达式语法正则表达式(RE)指定一个与之匹配的字符集合;本模块所提供的函数,将可以用来检查所给的字符串是否与指定的正则表达式匹配。正则表达式可以被连接,从而形成新的正则表达式;例如 A 和 B 都是正则表达式,那么 AB 也是正则表达式。 一般地, 如果字符串 p 与 A 匹配, q 与 B 匹配的话, 那么字符串 pq 也会与 AB 匹配,但 A 或者 B 里含有边界限定条件或者命名组操作的情况除外。也就是说,复杂的正则表达式可以用简单的连接而成。正则表达式可以包含特殊字符和普通字符,大部分字符比如A,a和0都是普通字符,如果做为

5、正则表达式,它们将匹配它们本身。由于正则表达式可以连接,所以连接多个普通字符而成的正则表达式 last 也将匹配last。 (后面将用不带引号的表示正则表达式, 带引号的表示字符串)下面就来介绍正则表达式的特殊字符:.点号,在普通模式,它匹配除换行符外的任意一个字符;如果指定了 DOTALL 标记,匹配包括换行符以内的任意一个字符。尖尖号,匹配一个字符串的开始,在 MULTILINE 模式下,也将匹配任意一个新行的开始。$美元符号,匹配一个字符串的结尾或者字符串最后面的换行符,在 MULTILINE 模式下,也匹配任意一行的行尾。也就是说,普通模式下,foo.$去搜索foo1nfoo2n只会找

6、到foo2,但是 MULTILINE 模式,还能找到fool,而且就用一个去搜索foon的话,会找到两个空的匹配:一个是最后的换行符,一个是字符串的结尾,演示: re.findall(foo.$),foolnfoo2n)foo2 re.findall(foo.$),foolnfoo2n,re.MULTILINE)fool,foo2 re.findall($),foon),*星号,指定将前面的 RE 重复 0 次或者任意多次,而且总是试图尽量多次地匹配。+加号,指定将前面的 RE 重复 1 次或者任意多次,而且总是试图尽量多次地匹配?问号,指定将前面的 RE 重复 0 次或者 1 次,如果有的话

7、,也尽量匹配 1 次。 ?+?,?从前面的描述可以看到*,+和?都是贪婪的,但这也许并不是我们说要的,所以,可以在后面加个问号,将策略改为非贪婪,只匹配尽量少的 RE。示例,体会两者的区别:re.findall(,title)H1titlere.findall(,title)H1,/H1mm 是一个数字,指定将前面的 RE 重复 m 次。m,nm 和 n 都是数字, 指定将前面的 RE 重复 m 到 n 次, 例如 a3,5匹配 3 到 5 个连续的 a。 注意,如果省略 m,将匹配 0 到 n 个前面的 RE;如果省略 n,将匹配 n 到无穷多个前面的 RE;当然中间的逗号是不能省略的,不然

8、就变成前面那种形式了。m,n?前面说的m,n,也是贪婪的,a3,5如果有 5 个以上连续 a 的话,会匹配 5 个,这个也可以通过加问号改变。a3,5?如果可能的话,将只匹配 3 个 a。反斜杆,转义*,?等特殊字符,或者指定一个特殊序列(下面会详述)由于之前所述的原因,强烈建议用 raw 字符串来表述正则。方括号,用于指定一个字符的集合。可以单独列出字符,也可以用-连接起止字符以表示一个范围。特殊字符在中括号里将失效,比如akm$就表示字符a,k,m,或$,在这里$也变身为普通字符了。a-z匹配任意一个小写字母,a-zA-Z0-9匹配任意一个字母或数字。如果你要匹配或-本身,你需要加反斜杆转

9、义,或者是将其置于中括号的最前面,比如可以匹配 T你还可以对一个字符集合取反,以匹配任意不在这个字符集合里的字符,取反操作用一个A放在集合的最前面表示,放在其他地方的A将不会起特殊作用。例如A5将匹配任意不是5的字符;AA将匹配任意不是A的字符。注意:在中括号里,+、*、(、)这类字符将会失去特殊含义,仅作为普通字符。反向引用也不能在中括号内使用。T管道符号,A 和 B 是任意的 RE,那么 A|B 就是匹配 A 或者 B 的一个新的 RE。任意个数的 RE都可以像这样用管道符号间隔连接起来。这种形式可以被用于组中(后面将详述)。对于目标字符串,被|分割的 RE 将自左至右一一被测试,一旦有一

10、个测试成功,后面的将不再被测试,即使后面的 RE 可能可以匹配更长的用,换句话说,|操作符是非贪婪的。要匹配字面意义上的|,可以用反斜杆转义:|,或是包含在反括号内:|。(.)匹配圆括号里的 RE 匹配的内容,并指定组的开始和结束位置。组里面的内容可以被提取,也可以采用number 这样的特殊序列,被用于后续的匹配。要匹配字面意义上的(和),可以用反斜杆转义:(、),或是包含在反括号内:(、)。(?.)这是一个表达式的扩展符号。 ?后的第一个字母决定了整个表达式的语法和含义, 除了 (?P.)以外,表达式不会产生一个新的组。下面介绍几个目前已被支持的扩展:(?iLmsux)i、L、m、s、u、

11、x里的一个或多个字母。表达式不匹配任何字符,但是指定相应的标志:re.I(忽略大小写)、re.L(依赖 locale)、re.M(多行模式)、re.S(.匹配所有字符)、re.U(依赖 Unicode)、re.X(详细模式)。关于各个模式的区别,下面会有专门的一节来介绍的。使用这个语法可以代替在 pile()的时候或者调用的时候指定 flag 参数。例如,上面举过的例子,可以改写成这样(和指定了 re.MULTILINE 是一样的效果):re.findall(?m)(foo.$),foolnfoo2n)fool,foo2另外,还要注意(?x)标志如果有的话,要放在最前面。(?:.)匹配内部的

12、RE 所匹配的内容,但是不建立组。(?P.)和普通的圆括号类似,但是子用匹配到的内容将可以用命名的 name 参数来提取。组的 name必须是有效的 python 标识符,而且在本表达式内不重名。命名了的组和普通组一样,也用数字来提取,也就是说名字只是个额外的属性。演示一下:m=re.match(?Pa-zA-Z_w*),abc=123)m.group(var)abcm.group(1)abc(?P=name)匹配之前以 name 命名的组里的内容。演示一下:re.match(?Pw*).*,xxx)#这个不匹配re.match(?Pw*).*,xxx)#这个匹配(?#.)注释,圆括号里的内容

13、会被忽略。(?=.)如果.匹配接下来的字符, 才算匹配, 但是并不会消耗任何被匹配的字符。 例如 Isaac(?=Asimov)只会匹配后面跟着Asimov的Isaac,这个叫做“前瞻断言”。(?!.)和上面的相反,只匹配接下来的字符串不匹配.的申,这叫做“反前瞻断言”。(?=.)只有当当前位置之前的字符串匹配.,整个匹配才有效,这叫“后顾断言”。(?m=re.search(?m.group(0)egg(?!.)同理,这个叫做“反后顾断言”,子 RE 需要固定长度的,含义是前面的字符串不匹配.整个才算匹配。(?(id/name)yes-pattern|no-pattern)如有由 id 或者

14、name 指定的组存在的话,将会匹配 yes-pattern,否则将会匹配 nopattern,通常情况下 no-pattern 也可以省略。例如:()可以匹配和,但是不会匹酉己unistr=uu2076u2084abcprintunistr?abcprintre.findall(d+,unistr,re.U)0?D和d 相反,不多说了s当未指定 UNICODE 和 LOCALE 这两个标志位时,匹配任何空白字符,等效于tnrfv。如果指定了 LOCALE,则还要加 LOCALE 相关的空白字符;如果指定了 UNICODE,还要加上 UNICODE 空白字符, 如较常见的空宽度连接空格 (uF

15、EFF) 、 零宽度非连接空格 (u200B)等。S和s 相反,也不多说。w当未指定 UNICODE 和 LOCALE 这两个标志位时,等效于a-zA-Z0-9。当指定了 LOCALE时, 为 0-9_ 加上当前 LOCAL 指定的字母。 当指定了 UNICODE 时, 为 0-9_ 力口上 UNICODE库里的所有字母。W和w 相反,不多说。Z只匹配字符串的结尾。匹配之于搜索python 提供了两种基于正则表达式的操作:匹配(match)从字符串的开始检查字符用是否个正则匹配。而搜索(search)检查字符用任意位置是否有匹配的子用(perl 默认就是如此)。注意,即使 search 的正则

16、以A开头,match 和 search 也还是有许多不同的。re.match(c,abcdef)#不匹配re.search(c,abcdef)#匹酉己模块的属性和方法pile(pattern,flags)把一个正则表达式 pattern 编译成正则对象,以便可以用正则对象的 match 和 search方法。得到的正则对象的行为(也就是模式)可以用 flags 来指定,值可以由几个下面的值 OR 得到。以下两段内容在语法上是等效的:prog=pile(pattern)result=prog.match(string)result=re.match(pattern,string)区别是,用了 p

17、ile 以后,正则对象会得到保留,这样在需要多次运用这个正则对象的时候,效率会有较大的提升。再用上面用过的例子来演示一下,用相同的正则匹配相同的字符串,执行 100 万次,就体现出 compile 的效率了(数据来自我那 1.86GCPU 的神舟本本):timeit.timeit(.setup=importre;reg=pile(?Pw*).*),.stmt=reg.match(xxx),.number=1000000)1.2062149047851562timeit.timeit(.setup=importre,.stmt=re.match(?Pw*).*,xxx),.number=1000

18、000)4.4380838871002197re.Ire.IGNORECASE让正则表达式忽略大小写,这样一来,A-Z也可以匹配小写字母了。此特性和 locale 无关。re.Lre.LOCALE让w、W、b、B、s 和S 依赖当前的 locale。re.Mre.MULTILINE影响 w 和$的行为,指定了以后,A会增加匹配每行的开始(也就是换行符后的位置);$会增加匹配每行的结束(也就是换行符前的位置)。re.Sre.DOTALL影响.的行为,平时.匹配除换行符以外的所有字符,指定了本标志以后,也可以匹配换行符re.Ure.UNICODE让w、W、b、B、d、D、s 和S 依赖 Unico

19、de 库。re.Xre.VERBOSE运用这个标志,你可以写出可读性更好的正则表达式:除了在方括号内的和被反斜杠转义的以外的所有空白字符,都将被忽略,而且每行中,一个正常的井号后的所有字符也被忽略,这样就可以方便地在正则表达式内部写注释了。也就是说,下面两个正则表达式是等效的:a=pile(r”d+#theintegralpart.#thedecimalpointd*#somefractionaldigits,re.X)b=pile(rd+.d*)re.search(pattern,string,flags)扫描string,看是否有个位置可以匹配正则表达式pattern。 如果找到了, 就返

20、回一个MatchObject的实例,否则返回 None,注意这和找到长度为 0 的子用含义是不一样的。搜索过程受 flags 的影响。re.match(pattern,string,flags)如果字符串 string 的开头和正则表达式 pattern 匹配的话,返回一个相应的 MatchObject 的实例,否则返回 None注意:要在字符串的任意位置搜索的话,需要使用上面的 search()。re.split(pattern,string,maxsplit=0)用匹配 pattern 的子用来分割 string,如果 pattern 里使用了圆括号,那么被 pattern 匹配到的用也将

21、作为返回值列表的一部分。如果 maxsplit 不为 0,则最多被分割为 maxsplit 个子用,剩余部分将整个地被返回。re.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.如果正则有圆括号,并且可以匹配到字符串的开始位置的时候,返回值的第一项,会多出一个空字符串。匹配到字符结尾也是同样的道理:re.split(W+),.words,words

22、.),.,words,words,.,注意,split 不会被零长度的正则所分割,例如:re.split(x*,foo)foore.split(?m)A$,foonnbarn)foonnbarnre.findall(pattern,string,flags)以列表的形式返回 string 里匹配 pattern 的不重叠的子用。string 会被从左到右依次扫描,返回的列表也是从左到右一次匹配到的。如果 pattern 里含有组的话,那么会返回匹配到的组的列表;如果 pattern 里有多个组,那么各组会先组成一个元组,然后返回值将是一个元组的列表。由于这个函数不会涉及到 MatchObjec

23、t 之类的概念,所以,对新手来说,应该是最好理解也最容易使用的一个函数了。下面就此来举几个简单的例子:#简单的 findallre.findall(w+,hello,world!)hello,world#这个返回的就是元组的列表re.findall(d+).(d+).(d+).(d+),MyIPis192.168.0.2,andyouris192.168.0.3.)(192,168,0,2),(192,168,0,3)re.finditer(pattern,string,flags)和上面的 findall()类似,但返回的是 MatchObject 的实例的迭代器。还是例子说明问题:form

24、inre.finditer(w+,hello,world!):.printm.group()helloworldre.sub(pattern,repl,string,count)替换,将 string 里,匹配 pattern 的部分,用 repl 替换掉,最多替换 count 次(剩余的匹配将不做处理),然后返回替换后的字符串。如果 string 里没有可以匹配 pattern 的用,将被原封不动地返回。repl 可以是一个字符串,也可以是一个函数(也可以参考我以前的例子)。如果 repl 是个字符串,则其中的反斜杆会被处理过,比如n 会被转成换行符,反斜杆加数字会被替换成相应的组,比如6

25、表示 pattern 匹配到的第 6 个组的内容。例子:re.sub(rdefs+(a-zA-Z_a-zA-Z_0-9*)s*(s*):,.rstaticPyObject*npy_1(void)n,.defmyfunc():)staticPyObject*npy_myfunc(void)n如果 repl 是个函数,每次 pattern 被匹配到的时候,都会被调用一次,传入一个匹配到的MatchObject 对象,需要返回一个字符串,在匹配到的位置,就填入返回的字符串。例子:defdashrepl(matchobj):.ifmatchobj.group(0)=-:return.else:retu

26、rn-re.sub(-1,2,dashrepl,pro-gram-files)pro-gramfiles零长度的匹配也会被替换,比如:re.sub(x*,-,abcxxd)-a-b-c-d-特殊地,在替换字符串里,如果有g 这样的写法,将匹配正则的命名组(前面介绍过的,(?P.)这样定义出来的东西)。g 这样的写法,也是数字的组,也就是说,g 一般和2 是等效的,但是万一你要在2 后面紧接着写上字面意义的 0,你就不能写成20 了(因为这代表第 20 个组), 这时候必须写成g0,另外,g 代表匹配到的整个子用。例子:re.sub(-(d+)-,-g0g,a-11-b-22-c)a-110-1

27、1-b-220-22-cre.subn(pattern,repl,string,count)跟上面的 sub()函数一样,只是它返回的是一个元组(新字符串,匹配到的次数),还是用例子说话:re.subn(-(d+)-,-g0g,a-11-b-22-c)(a-110-11-b-220-22-c,2)re.escape(string)把 string 中,除了字母和数字以外的字符,都加上反斜杆。printre.escape(abc123_#$)#$exceptionre.error如果字符串不能被成功编译成正则表达式或者正则表达式在匹配过程中出错了,都会抛出此异常。但是如果正则表达式没有匹配到任何

28、文本,是不会抛出这个异常的。正则对象正则对象由 pile()返回。它有如下的属性和方法。match(string,pos,endpos)作用和模块的 match()函数类似,区别就是后面两个参数。pos 是开始搜索的位置,默认为0oendpos 是搜索的结束位置,如果 endpos 比 pos 还小的话,结果肯定是空的。也就是说只有 pos 到 endpos-1 位置的字符串将会被搜索。例子:pattern=pile(o)pattern.match(dog)#开始位置不是 o,所以不匹配pattern.match(dog,1)#第二个字符是 o,所以匹配search(string,pos,en

29、dpos)作用和模块的 search()函数类似,pos 和 endpos 参数和上面的 match()函数类似。split(string,maxsplit=0)findall(string,pos,endpos)finditer(string,pos,endpos)sub(repl,string,count=0)subn(repl,string,count=0)这几个函数,都和模块的相应函数一致。flags编译本 RE 时,指定的标志位,如果未指定任何标志位,则为0opattern=pile(o,re.S|re.U)pattern.flagsgroupsRE 所含有的组的个数。groupin

30、dex一个字典,定义了命名组的名字和序号之间的关系。例子:这个正则有 3 个组,如果匹配到,第一个叫区号,最后一个叫分机号,中间的那个未命名pattern=pile(?Pd+)-(d+)-(?Pd+)pattern.groups3pattern.groupindexfenjihao:3,quhao:1pattern建立本 RE 的原始字符串,相当于源代码了,呵呵。还是上面这个正则,可以看到,会原样返回:printpattern.pattern(?Pd+)-(d+)-(?Pd+)Match对象re.MatchObject 被用于布尔判断的时候,始终返回 True,所以你用 if 语句来判断某个

31、match()是否成功是安全的。它有以下方法和属性:expand(template)用 template 做为模板,将 MatchObject 展开,就像 sub()里的行为一样,看例子:m=re.match(a=(d+),a=100)m.expand(aboveaisg)aboveais100m.expand(raboveais1)aboveais100group(group1,.)返回一个或多个子组。如果参数为一个,就返回一个子用;如果参数有多个,就返回多个子用注册的元组。如果不传任何参数,效果和传入一个 0 一样,将返回整个匹配。如果某个 groupN 未匹配到,相应位置会返回 None

32、。如果某个 groupN 是负数或者大于 group 的总数,则会抛出 IndexError 异常。m=re.match(r(w+)(w+),IsaacNewton,physicist)如果有其中有用(?P.)这种语法命名过的子用的话,相应的 groupN 也可以是名字字符申。例如:m=re.match(r(?Pw+)(?Pw+),MalcolmReynolds)m.group(first_name)Malcolmm.group(last_name)Reynolds如果某个组被匹配到多次,那么只有最后一次的数据,可以被提取到:m=re.match(r”(.)+,a1b2c3)#匹配到 3 次m.group(1)#返回的是最后一次c3groups(default)返回一个由所有匹配到的子用组成的元组。default 参数,用于给那些没有匹配到的组做默认值,它的默认值是 None例如:m=re.match(r(d+).(d+),24.1632)m.groups()(24,1632)default 的作用:m=re.match(r(d+).?(d+)?,24)m.groups()#第二个默认是 Nonem.group(0)IsaacNewtonm.group(1)Isaa

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论