python正则表达式

在python在,正则表达式的功能在「re」模块中,如果要使用正则表达式,请先引用:

1 import re

正则表达中,分为普通字符和特殊字符(metacharacter),普通字符如:a,b,hello等,特殊字符如:

. ^ $ * + ? { } [ ] \ | ( )

[ ]

包括在[]中字符序列,表示只要匹配其中的一个字符就可以. 如[abdef]可以匹配a,b,d,e,f. 也可以表示成匹配一组范围内的元素,如[0-9]匹配数字0,1,2...9, [a-z] 匹配a,b,c...z. 

对于[^hello]形式,其中的"^"是"取非,相反"的意思,就是除了h,e,l,o 之外的任何字符. 

Note: 包含在[]中的特殊字符此时变成了普通字符,如[hello$]可以匹配h,e,l,o,$, 此时$就是普通字符.

\

 "\" 是正则表达式中最关键重要的特殊字符, 当它和其它一些普通字符组合在一起时,可以形成很多有意义的表达方式, 如"\d"表示一个0~1的数字. 当然, 如果你本来就想一些特殊字符的表现形式时, 可以用"\"进行转义, 如"\\" 就匹配"\"本身, "\[" 匹配"[" 本身,经过转义后,特殊字符(medacharater)就变成正常的普通字符串的形式. 

下面就来看一些组合表达式可以匹配的集合字符:

  • \d 匹配十进制形式的数字,相当于 [0-9]
  • \D 匹配任何数字形式的字符,相当于 [^0-9]
  • \s 匹配任何的空白字符, 相当于 [ \t\n\r\f\v]
  • \S 匹配任何的空白字符,相当于[^ \t\n\r\f\v]
  • \w 匹配任何的字母数字字符,相当于 [a-zA-Z0-9]\
  • \W 你应该懂的了.......

.

 "." 点表示匹配任意的字符, 除了换行符以外,如"\r","\n"; 当然,如果想让"."匹配真正的任意字符, 可以切换到「re.DOTALL」模式中,此时 . 也可以匹配换行符.

重复元素

有时候我们要匹配查找某个元素出现几次的对象, 所以重复次数的表达方法就显得有点重要了. 

"*" 表示0到多次. 如 "ca*t" 可以匹配 cat, ct, caat, caa....t , 字符"a"要以出现0次,1次,或者任何多次. 

对于匹配的次数,在python中默认是贪婪的. 什么是贪婪的意思呢? 就是说正则表达式引擎计算匹配的时候, 尽可能匹配最多的次数. 如果还不能理解, 请看下面的例子:

1 regular = r'(\w*)123'

2 p = re.compile(regular)

3 m = p.search('hello123123')

 正则表达式 "(\w*)123" 匹配的文本可以这么描述: 匹配以任意字母数字字符重复出现过n次后,再出现"123"文本. 那么现在用这个表达式去匹配文本 "hello123123" 时,匹配到的文本会是什么呢? 是"hello" 还是 "hello123"呢? 如果是贪婪模式下,那么结果就是 "hello123", 如果是非贪婪模式,结果就是 "hello". 贪婪, 顾名思义, 就是尽可能往后匹配, 只要能满足正则表达式的要求; 非贪婪, 就是只要遇到了满足后一匹配元素的的要求,立即停止继续向后匹配.

其它一些表示次数的特殊字符,如 "+" 表示出现1次以上, 它和 "*" 的区别是: "*" 可以表示从来不出现,即0次; 而 "+" 至少出现一次. 

还有 "?" 表示出现一次或不出现, 有一种"可选"的意思.

还有一种可以控制次数具体数量的表示方式, {m,n} 表示次数要大于或等于 m 但是要小于或等于 n . 如 a/{1,3}b 可以匹配 a/b, a//b, a///b, 但是不能匹配 ab, a////b. m 和 n 可以省略不写,如 {m,} 表示次数要大于或等于m并且没有上限限制, {,n} 表示次数要小于或等于n次,最小可以是0. 现在我们发现, "*" 可以用 {0,} 表示, "+" 可以用 {1,} 表示, "?" 可以用 {0,1}表示. 但是,我们一般用"*,+,?"表示,因为一个原因:简单. 

反斜杠的烦恼

在正则表达式里, 我们知道 "\" 有着特殊的用途, 它和一些字符的组合有着特殊的含义, 然而不巧的是, "\" 在python语言中字符串里本身也有着特殊的用途, 它起到表达一些特殊的字符转义, 如 "\n" 表示 "换行" 字符. 这样的话我们如果要写一个这样的正则表达式, 它能匹配 "\abc":

regular = "\\abc"

上面的表达式在python虚拟机运运行的时候其内存结构去是这样: "\abc" , 因为它 \\ 是对 \ 的转义, 表示一个字符 "\". 所以我们要这样写正则表达式:

regular = "\\\\abc"

 才会生成字符串: "\\abc" , 这样的话文本"\abc" 就可以匹配之. 所以为了避免在写正则表达式写这么多的 "\" 字符, python提供了一种机制,叫做 raw string ,原始字符, 即最终的字符结构和代码里的表现形式一样.

regular = r"\abc"

 用 r 作为字符串的前缀即可. 

执行匹配操作

 当我们执行下面代码后:

regular = r'\d+'

pattern = re.compile(regular)

 得到的 pattern 就是经过编译后的模式对象,用它来执行相关的匹配操作. 它有如下一此匹配的方法:

  • match()  是否匹配一个文本(从文本最开始处开始匹配)
  • search() 扫描整个文本看是否匹配(任意位置都可以)
  • findall()  列出所有匹配的字符串,并以列表list格式返回
  • finderiter() 和findall一样也是匹配所有的并返回,但不同的是findall是返回之前要形成最后的结果列表,即要匹配出所有的字符串后再返回; 而finditer()是返回一个迭代器, 让用户自己每次匹配单个并执行相关操作,有点像游标的概念. 所以可以这样理解, 如果从性能考虑的话用finditer.

正则表达式编译时标识

 正则表达式编译时可以设置相关的参数标识用以生成不同特性的匹配模式, 比如我们现在要匹配单词时不区分大小写,可以这样:

pattern = re.compile(r'hello',re.IGNORECASE)

上面的表达式可以匹配 hello, HELLO, heLLo,HeLlO,等.

以下介绍另外的5种标识

re.LOCALE

如果设置re.IGNORECASE即匹配时不区分大小写, 但是所谓的大小写就是指[a-zA-Z]这些字母, 如果在其它国家语言里也有大小写的话, 那么现在除了要设置IGNORECASE外, 还要设置LOCALE标识. 不过好像在汉语里好像没有什么用.

re.MUTILINE

我们前面讲过 ^ 和 $ 分别表示匹配是以开文件的开始位置和结束位置. 如 "^hello$" 只匹配 "hello" ,而不会匹配 "hello\nhello" . 如果要匹配多行的文本,则要设置该标识,如下代码:

pattern = re.compile(r'^hello$', re.MULTILINE)

r = '''hello

hello

'''

print pattern.match(r) # 设置MUTILINE模式,此时可以匹配多行

 re.DOTALL

我们知道 "." 可以匹配任意的字符, 但是默认是不匹配换行的,如下:

pattern = re.compile(r'hello.*a')

r = '''helloxxxx

xa

'''

print pattern.match(r) # 此时不能匹配, 因为helloxxxxxa被分割成两行, "." 默认是不能匹配换行的

 如果设置DOTALL 模式就可以了:

pattern = re.compile(r'hello.*a', re.DOTALL)

r = '''helloxxxx

xa

'''

print pattern.match(r) # 此时可以匹配成功

 re.UNICODE 

使这些 \w, \W, \b, \B, \d, \D, \s and \S 能依赖于unicode字符库匹配. 如果上面一句话不能理解, 我们举个例子. 大家知道 \w 是匹配字母和数字形式的字符, 但是我们希望它可以匹除了[a-zA-Z]之外的任何, 看如下一段代码:

pattern = re.compile(r'hello, \w{2} , haha! ')

print pattern.match(u"hello, 你好 , haha! ") #

上面是匹配会失败, 因为 \w 默认只是英文字母. 改成如下即可:

pattern = re.compile(r'hello, \w{2} , haha! ', re.UNICODE)

print pattern.match(u"hello, 你好 , haha! ") 

re.VERBOSE

有时候我们要写一个比较复杂的正则表达式, 而为了程序的可读性, 希望对表达式的每个部分添加一些注解, 此时可以设置 VERBOSE 模式, 即表达式冗余模式. 如下:

pattern = re.compile(r'''hello,\  # 在VERBOSE模式下,所有空白都会忽略,如换行,空格,制表等,如果要匹配空格的话,如空格,需要转义: \空格

                        \w{2} # 这里要匹配两个字哦

                        ,haha!

                        ''', re.VERBOSE)

print pattern.match(u"hello, TT,haha! ") #

 

介绍更多特殊字符 

 |

 有时我们要匹配两个表达式中的任何一个, 就像逻辑与操作:

pattern = re.compile(r'(hello|byby) , tony!')



print pattern.search("hello , tony!") #可以匹配hello

print pattern.search("byby , tony!") #也可以匹配byby

 

 \A 和 \Z

 \A和^, \Z和$ 有着一样的作用, 不同的是在MUTILINE模式下, \A和\Z仍然只会以文本的开头和结尾处匹配, 不会在每行中匹配.

pattern = re.compile(r'^hello$', re.MULTILINE)

print pattern.search("hello\nhello") #在多行模式下, ^hello$ 可以从多行中匹配



pattern = re.compile(r'\Ahello\Z', re.MULTILINE)

print pattern.search("hello\nhello") #在多行模式下, \Ahello\Z 仍然只能匹配文本的开头和结尾, 此时匹配失败

 

\b 和 \B 

\b 表示"单词边界"的意思, 如 "\bhello\b" 只能匹配 hello ,不能匹配 YhelloM , 因为hello必需是独立的单词, 不被任何的其它单词包围, 即两边有单词边界分隔, 边界可以有很多种定义, 如空格,换行,等其它非字母数字的字符.

\B 和 \b 恰好相反, 表示此处不存在边界, 即被其它单词包括.

分组 Grouping

 前面所讲解的内容,主要是围绕着是否可以匹配成功. 现实中我们经常要从一段文本中抽取出我们感兴趣的一些来分析,此时分组grouping 就显得特别有用了. 在正则表达式中的分组, 跟数学中的分组很类似, 都是把一组相关的逻辑运算归纳到一起. 其实在前面介绍 | 符号时已经用到过分组了.

 可以把分组匹配的信息抽取出来,通过group(index)来操作. 如下: 

pattern = re.compile(r'(a(\d+)(b)),hello')

match = pattern.search("a138b,hello byby")

print match.groups()  # ('a138b', '138', 'b') 以元组的形式返回所有的分组

print match.group(0)  # a138b,hello 每个匹配都会存在一个以0为索引的分组,即全部匹配结果

print match.group(1)  # a138b 返回第一个分组,

print match.group(2)  # 138 返回第二个分组

print match.group(3)  # b

 

传入group(index)中的位置索引index对应于正则表达式中从往右遇到的括号位置.

分组在正则表达式中可以通过位置索引进行多次指定, 如可以用 \index 的方式表示指定的分组, 其中index表示之前分组中的位置数 , 见如下示例:

pattern = re.compile(r'(\b\w+\b) hello \1')  # \1 表示此时要出现和前面分组1,即(\b\w+\b)一样的内容

match = pattern.search("yy hello yy")

Non-capturing and Named Groups

不知道这个标题准确的翻译, 但是一看英文应该知道表达的意思, non-capturing Groups, 即非占位分组, 就是不占用此分组在整个正则表达式中的索引位置. Named Groups, 就是命名式分组, 就是给此分组取个别名. 还是看些代码片段就知道表达的意思了:

pattern = re.compile(r'hello(?:[abc]+)yy')  #

match = pattern.search("helloabcyy")



print match.group(1)  # 此时并不能匹配 abc , 因为(?:...) 的形式就是忽略此个分组, 即Non-capturing

 

 对于 (?P<name>...) 形式的表达式, 就是Named Groups的形式. 这样我们在以后获取分组信息时就多了一条选择, 不但可以通过group(int)来获取指定数字位置的分组, 而且可以通过group(name)来获取指定名称的分组,看如下示例:

pattern = re.compile(r'hello(?P<alias>[abc]+)yy')  #

match = pattern.search("helloabcyy")



print match.group('alias')  # 此时是通过表达式中定义的名称"alias"来获取分组信息

 

 在上一节中我们讲过可以用 \index 的形式来指定前面定义的分组此时应该出现在这, 除了这种形式, 我们也可以用 (?P=name) 的形式来完成一样的事.

pattern = re.compile(r'hello(?P<alias>[abc]+)yy(?P=alias)')  

match = pattern.search("helloayya")  # 此时两个地方要一样才能匹配

 

Lookahead Assertions

假设现在我们有这样一个场景, 要匹配文件名后缀不是 "exe" 的文件, 怎样写这个正则表达式呢? 可以这样写: "\w+\.(?!exe$)(.*$)" , 对于 (?!...)  的表达形式, 它是一种 assertion , 凡是所有的assertion 都有一个特征, 就是引擎匹配扫描时不会消费任何文本字符, 它只是断言当前的文本是否匹配这个表达式(就是...所占的内容), 如果不匹配, 则继续余下的表达式扫描动作, 否则就会直接返回整个匹配失败.

pattern = re.compile(r'\w+\.(?!exe$)(.*$)')

match = pattern.search("abc.jpg")

print match.group(1)  # 打印 : jpg

match = pattern.search("abc.exe")

print match.group(1)  # 此时匹配失败

 

和 (?!...) 相反的表达式是 (?=...) 的形式. 后者指满足这个断言时才会继续接下来的匹配.

 

原文链接: http://docs.python.org/2.7/howto/regex.html#regex-howto

你可能感兴趣的:(python)