python正则表达式re模块详细介绍
本模块提供了和Perl⾥的正则表达式类似的功能,不关是正则表达式本⾝还是被搜索的字符串,都可以是Unicode字符,这点不⽤担⼼,python会处理地和Ascii字符⼀样漂亮。
正则表达式使⽤反斜杆(\)来转义特殊字符,使其可以匹配字符本⾝,⽽不是指定其他特殊的含义。这可能会和python字⾯意义上的字符串转义相冲突,这也许有些令⼈费解。⽐如,要匹配⼀个反斜杆本⾝,你也许要⽤'\\\\'来做为正则表达式的字符串,因为正则表达式要是\\,⽽字符串⾥,每个反斜杆都要写成\\。
你也可以在字符串前加上 r 这个前缀来避免部分疑惑,因为 r 开头的python字符串是 raw 字符串,所以⾥⾯的所有字符都不会被转义,⽐如r'\n'这个字符串就是⼀个反斜杆加上⼀字母n,⽽'\n'我们知道这是个换⾏符。因此,上⾯的'\\\\'你也可以写成r'\\',这样,应该就好理解很多了。可以看下⾯这段:
复制代码代码如下:
>>> import re
>>> s = ' 5c' #0x5c就是反斜杆
>>> print s
\
>>> re.match('\\\\', s) #这样可以匹配
<_sre.SRE_Match object at 0xb6949e20>
>>> re.match(r'\\', s) #这样也可以
<_sre.SRE_Match object at 0x80ce2c0>
>>> re.match('\\', s) #但是这样不⾏
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.6/re.py", line 137, in match
return _compile(pattern, flags).match(string)
File "/usr/lib/python2.6/re.py", line 245, in _compile
raise error, v # invalid expression
: bogus escape (end of line)
>>>
另外值得⼀提的是,re模块的⽅法,⼤多也就是RegexObject对象的⽅法,两者的区别在于执⾏效率。这个在最后再展开吧。正则表达式语法
正则表达式(RE)指定⼀个与之匹配的字符集合;本模块所提供的函数,将可以⽤来检查所给的字符串是否与指定的正则表达式匹配。
正则表达式可以被连接,从⽽形成新的正则表达式;例如A和B都是正则表达式,那么AB也是正则表达式。⼀般地,如果字符串p与A匹配,q与B匹配的话,那么字符串pq也会与AB匹配,但A或者B⾥含有边界限定条件或者命名组操作的情况除外。也就是说,复杂的正则表达式可以⽤简单的连接⽽成。
正则表达式可以包含特殊字符和普通字符,⼤部分字符⽐如'A','a'和'0'都是普通字符,如果做为正则表达式,它们将匹配它们本⾝。由于正则表达式可以连接,所以连接多个普通字符⽽成的正则表达式last也将匹配'last'。(后⾯将⽤不带引号的表⽰正则表达式,带引号的表⽰字符串)
下⾯就来介绍正则表达式的特殊字符:
'.'
点号,在普通模式,它匹配除换⾏符外的任意⼀个字符;如果指定了 DOTALL 标记,匹配包括换⾏符以内的任意⼀个字符。'^'
尖尖号,匹配⼀个字符串的开始,在 MULTILINE 模式下,也将匹配任意⼀个新⾏的开始。
'$'
美元符号,匹配⼀个字符串的结尾或者字符串最后⾯的换⾏符,在 MULTILINE 模式下,也匹配任意⼀⾏的⾏尾。也就是说,普通模式下,foo.$去搜索'foo1\nfoo2\n'只会到'foo2′,但是在 MULTILINE 模式,还能到 ‘foo1′,⽽且就⽤⼀个 $ 去搜
索'foo\n'的话,会到两个空的匹配:⼀个是最后的换⾏符,⼀个是字符串的结尾,演⽰:
复制代码代码如下:
>>> re.findall('(foo.$)', 'foo1\nfoo2\n')
['foo2']
>>> re.findall('(foo.$)', 'foo1\nfoo2\n', re.MULTILINE)
['foo1', 'foo2']
>>> re.findall('($)', 'foo\n')
['', '']
'*'
星号,指定将前⾯的RE重复0次或者任意多次,⽽且总是试图尽量多次地匹配。
'+'
加号,指定将前⾯的RE重复1次或者任意多次,⽽且总是试图尽量多次地匹配。
'?'
问号,指定将前⾯的RE重复0次或者1次,如果有的话,也尽量匹配1次。
*?, +?, ??
从前⾯的描述可以看到'*','+'和'?'都是贪婪的,但这也许并不是我们说要的,所以,可以在后⾯加个问号,将策略改为⾮贪婪,只匹配尽量少的RE。⽰例,体会两者的区别:
复制代码代码如下:
>>> re.findall('<(.*)>', '<H1>title</H1>')
['H1>title</H1']
>>> re.findall('<(.*?)>', '<H1>title</H1>')
['H1', '/H1']
{m}
m是⼀个数字,指定将前⾯的RE重复m次。
{m,n}
m和n都是数字,指定将前⾯的RE重复m到n次,例如a{3,5}匹配3到5个连续的a。注意,如果省略m,将匹配0到n个前⾯的RE;如果省略n,将匹配n到⽆穷多个前⾯的RE;当然中间的逗号是不能省略的,
不然就变成前⾯那种形式了。
{m,n}?
前⾯说的{m,n},也是贪婪的,a{3,5}如果有5个以上连续a的话,会匹配5个,这个也可以通过加问号改变。a{3,5}?如果可能的话,将只匹配3个a。
'\'
反斜杆,转义'*','?'等特殊字符,或者指定⼀个特殊序列(下⾯会详述)
由于之前所述的原因,强烈建议⽤raw字符串来表述正则。
[]
⽅括号,⽤于指定⼀个字符的集合。可以单独列出字符,也可以⽤'-'连接起⽌字符以表⽰⼀个范围。特殊字符在中括号⾥将失效,⽐如[akm$]就表⽰字符'a','k','m',或'$',在这⾥$也变⾝为普通字符了。[a-z]匹配任意⼀个⼩写字母,[a-zA-Z0-9]匹配任意⼀个字母或数字。如果你要匹配']'或'-'本⾝,你需要加反斜杆转义,或者是将其置于中括号的最前⾯,⽐如[]]可以匹配']'
你还可以对⼀个字符集合取反,以匹配任意不在这个字符集合⾥的字符,取反操作⽤⼀个'^'放在集合的最前⾯表⽰,放在其他地⽅的'^'将不会起特殊作⽤。例如[^5]将匹配任意不是'5'的字符;[^^]将匹配任意不是'^'的字符。
注意:在中括号⾥,+、*、(、)这类字符将会失去特殊含义,仅作为普通字符。反向引⽤也不能在中括号内使⽤。
'|'
管道符号,A和B是任意的RE,那么A|B就是匹配A或者B的⼀个新的RE。任意个数的RE都可以像这样⽤管道符号间隔连接起来。这种形式可以被⽤于组中(后⾯将详述)。对于⽬标字符串,被'|'分割的RE将⾃左⾄右⼀⼀被测试,⼀旦有⼀个测试成功,后⾯的将不再被测试,即使后⾯的RE可能可以匹配更长的串,换句话说,'|'操作符是⾮贪婪的。要匹配字⾯意义上的'|',可以⽤反斜杆转义:\|,或是包含在反括号内:[|]。
(...)
匹配圆括号⾥的RE匹配的内容,并指定组的开始和结束位置。组⾥⾯的内容可以被提取,也可以采⽤\number这样的特殊序列,被⽤于后续的匹配。要匹配字⾯意义上的'('和')',可以⽤反斜杆转义:\(、\),或是包含在反括号内:[(]、[)]。
(?...)
这是⼀个表达式的扩展符号。'?'后的第⼀个字母决定了整个表达式的语法和含义,除了(?P...)以外,表达式不会产⽣⼀个新的组。下⾯介绍⼏个⽬前已被⽀持的扩展:
(?iLmsux)
'i'、'L'、'm'、's'、'u'、'x'⾥的⼀个或多个字母。表达式不匹配任何字符,但是指定相应的标志:re.I(忽略⼤⼩写)、re.L(依赖locale)、re.M(多⾏模式)、re.S(.匹配所有字符)、re.U(依赖Unicode)、re.X(详细模式)。关于各个模式的区别,下⾯会有专门的
⼀节来介绍的。使⽤这个语法可以代替在repile()的时候或者调⽤的时候指定flag参数。
例如,上⾯举过的例⼦,可以改写成这样(和指定了re.MULTILINE是⼀样的效果):
复制代码代码如下:
>>> re.findall('(?m)(foo.$)', 'foo1\nfoo2\n')
['foo1', 'foo2']
另外,还要注意(?x)标志如果有的话,要放在最前⾯。
(?:...)
匹配内部的RE所匹配的内容,但是不建⽴组。
(?P<name>...)
和普通的圆括号类似,但是⼦串匹配到的内容将可以⽤命名的name参数来提取。组的name必须是有效的python标识符,⽽且在本表达式内不重名。命名了的组和普通组⼀样,也⽤数字来提取,也就是说名字只是个额外的属性。
演⽰⼀下:
复制代码代码如下:
>>> m=re.match('(?P<var>[a-zA-Z_]\w*)', 'abc=123')
>>> m.group('var')
'abc'
>>> m.group(1)
'abc'
(?P=name)
匹配之前以name命名的组⾥的内容。
演⽰⼀下:
复制代码代码如下:
>>> re.match('<(?P<tagname>\w*)>.*</(?P=tagname)>', '<h1>xxx</h2>') #这个不匹配
>>> re.match('<(?P<tagname>\w*)>.*</(?P=tagname)>', '<h1>xxx</h1>') #这个匹配
<_sre.SRE_Match object at 0xb69588e0>
(?#...)
注释,圆括号⾥的内容会被忽略。
(?=...)
如果 ... 匹配接下来的字符,才算匹配,但是并不会消耗任何被匹配的字符。例如 Isaac (?=Asimov) 只会匹配后⾯跟着
'Asimov' 的 'Isaac ',这个叫做“前瞻断⾔”。
(?!...)
和上⾯的相反,只匹配接下来的字符串不匹配 ... 的串,这叫做“反前瞻断⾔”。
(?<=...)
只有当当前位置之前的字符串匹配 ... ,整个匹配才有效,这叫“后顾断⾔”。字符串'abcdef'可以匹配正则(?<=abc)def,因为会后向查3个字符,看是否为abc。所以内置的⼦RE,需要是固定长度的,⽐如可以是abc、a|b,但不能是a*、a{3,4}。注意这种RE永远不会匹配到字符串的开头。举个例⼦,到连字符('-')后的单词:
复制代码代码如下:
>>> m = re.search('(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'
(?<!...)
同理,这个叫做“反后顾断⾔”,⼦RE需要固定长度的,含义是前⾯的字符串不匹配 ... 整个才算匹配。
(?(id/name)yes-pattern|no-pattern)
如有由id或者name指定的组存在的话,将会匹配yes-pattern,否则将会匹配no-pattern,通常情况下no-pattern也可以省略。例如:(<)?(\w+@\w+(?:\.\w+)+)(?(1)>)可以匹配 '<user@host>' 和 'user@host',但是不会匹配 '<user@host'。
下⾯列出以'\'开头的特殊序列。如果某个字符没有在下⾯列出,那么RE的结果会只匹配那个字母本⾝,⽐如,\$只匹配字⾯意
义上的'$'。
\number
匹配number所指的组相同的字符串。组的序号从1开始。例如:(.+) \1可以匹配'the the'和'55 55',但不匹配'the end'。这种序列在⼀个正则表达式⾥最多可以有99个,如果number以0开头,或是有3位以上的数字,就会被当做⼋进制表⽰的字符了。同时,这个也不能⽤于⽅括号内。
\A
只匹配字符串的开始。
\b
匹配单词边界(包括开始和结束),这⾥的“单词”,是指连续的字母、数字和下划线组成的字符串。注意,\b的定义是\w和\W 的交界,所以精确的定义有赖于UNICODE和LOCALE这两个标志位。
\B
和\b相反,\B匹配⾮单词边界。也依赖于UNICODE和LOCALE这两个标志位。
\d
未指定UNICODE标志时,匹配数字,等效于:[0-9]。指定了UNICODE标志时,还会匹配其他Unicode库⾥描述为字符串的符号。便于理解,举个例⼦(好不容易的例⼦啊,呵呵):
复制代码代码如下:
#\u2076\和u2084分别是上标的6和下标的4,属于unicode的DIGIT
>>> unistr = u'\u2076\u2084abc'
>>> print unistr
⁶₄abc
>>> print re.findall('\d+', unistr, re.U)[0]
⁶₄
\D
和\d相反,不多说了。
\s
当未指定UNICODE和LOCALE这两个标志位时,匹配任何空⽩字符,等效于[ \t\n\r\f\v]。如果指定了L
OCALE,则还要加LOCALE相关的空⽩字符;如果指定了UNICODE,还要加上UNICODE空⽩字符,如较常见的空宽度连接空格(\uFEFF)、零宽度⾮连接空格(\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的正则以'^'开头,match和search也还是有许多不同的。
复制代码代码如下:
>>> re.match("c", "abcdef") # 不匹配
>>> re.search("c", "abcdef") # 匹配
<_sre.SRE_Match object at ...>
模块的属性和⽅法
repile(pattern[, flags])
把⼀个正则表达式pattern编译成正则对象,以便可以⽤正则对象的match和search⽅法。
得到的正则对象的⾏为(也就是模式)可以⽤flags来指定,值可以由⼏个下⾯的值OR得到。
以下两段内容在语法上是等效的:
python正则表达式不包含
复制代码代码如下:
prog = repile(pattern)
result = prog.match(string)
result = re.match(pattern, string)
区别是,⽤了repile以后,正则对象会得到保留,这样在需要多次运⽤这个正则对象的时候,效率会有较⼤的提升。再⽤上⾯⽤过的例⼦来演⽰⼀下,⽤相同的正则匹配相同的字符串,执⾏100万次,就体现出compile的效率了(数据来⾃我那1.86G CPU的神⾈本本):
复制代码代码如下:
>>> timeit.timeit(
... setup='''import re; reg = repile('<(?P<tagname>\w*)>.*</(?P=tagname)>')''',
... stmt='''reg.match('<h1>xxx</h1>')''',
... number=1000000)
1.2062149047851562
>>> timeit.timeit(
... setup='''import re''',
... stmt='''re.match('<(?P<tagname>\w*)>.*</(?P=tagname)>', '<h1>xxx</h1>')''',
... number=1000000)
4.4380838871002197
re.I
re.IGNORECASE
让正则表达式忽略⼤⼩写,这样⼀来,[A-Z]也可以匹配⼩写字母了。此特性和locale⽆关。
re.L
re.LOCALE
让\w、\W、\b、\B、\s和\S依赖当前的locale。
re.M
re.MULTILINE
影响'^'和'$'的⾏为,指定了以后,'^'会增加匹配每⾏的开始(也就是换⾏符后的位置);'$'会增加匹配每⾏的结束(也就是换⾏符前的位置)。
re.S
re.DOTALL
影响'.'的⾏为,平时'.'匹配除换⾏符以外的所有字符,指定了本标志以后,也可以匹配换⾏符。
re.U
re.UNICODE
让\w、\W、\b、\B、\d、\D、\s和\S依赖Unicode库。
re.X
re.VERBOSE
运⽤这个标志,你可以写出可读性更好的正则表达式:除了在⽅括号内的和被反斜杠转义的以外的所有空⽩字符,都将被忽略,⽽且每⾏中,⼀个正常的井号后的所有字符也被忽略,这样就可以⽅便地在正则表达式内部写注释了。也就是说,下⾯两个正则表达式是等效的:
复制代码代码如下:
a = repile(r"""\d + # the integral part
\. # the decimal point
\d * # some fractional digits""", re.X)
b = repile(r"\d+\.\d*")
re.search(pattern, string[, flags])
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论