http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html
本文介绍了Python对于正则表达式的支持,包括正则表达式基础以及Python正则表达式标准库的完整介绍及使用示例。本文的内容不包括如何编写高效的正则表达式、如何优化正则表达式,这些主题请查看其他教程。
注意:本文基于Python2.4完成;如果看到不明白的词汇请记得百度谷歌或维基,whatever。
尊重作者的劳动,转载请注明作者及原文地址 >.<html
正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。如果已经在其他语言里使用过正则表达式,只需要简单看一看就可以上手了。
下图展示了使用正则表达式进行匹配的流程:
正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的,看下图中的示例以及自己多使用几次就能明白。
下图列出了Python支持的正则表达式元字符和语法:
正则表达式通常用于在文本中查找匹配的字符串。Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则相反,总是尝试匹配尽可能少的字符。例如:正则表达式"ab*"如果用于查找"abbbc",将找到"abbb"。而如果使用非贪婪的数量词"ab*?",将找到"a"。
与大多数编程语言相同,正则表达式里使用"\"作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\\"表示。同样,匹配一个数字的"\\d"可以写成r"\d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
正则表达式提供了一些可用的匹配模式,比如忽略大小写、多行匹配等,这部分内容将在Pattern类的工厂方法re.compile(pattern[, flags])中一起介绍。
Python通过re模块提供对正则表达式的支持。使用re的一般步骤是先将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得匹配结果(一个Match实例),最后使用Match实例获得信息,进行其他的操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# encoding: UTF-8
import
re
# 将正则表达式编译成Pattern对象
pattern
=
re.
compile
(r
'hello'
)
# 使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None
match
=
pattern.match(
'hello world!'
)
if
match:
# 使用Match获得分组信息
print
match.group()
### 输出 ###
# hello
|
re.compile(strPattern[, flag]):
这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。 第二个参数flag是匹配模式,取值可以使用按位或运算符'|'表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)与re.compile('(?im)pattern')是等价的。
可选值有:
1
2
3
4
|
a
=
re.
compile
(r
"""\d + # the integral part
\. # the decimal point
\d * # some fractional digits"""
, re.X)
b
=
re.
compile
(r
"\d+\.\d*"
)
|
re提供了众多模块方法用于完成正则表达式的功能。这些方法可以使用Pattern实例的相应方法替代,唯一的好处是少写一行re.compile()代码,但同时也无法复用编译后的Pattern对象。这些方法将在Pattern类的实例方法部分一起介绍。如上面这个例子可以简写为:
1
2
|
m
=
re.match(r
'hello'
,
'hello world!'
)
print
m.group()
|
re模块还提供了一个方法escape(string),用于将string中的正则表达式元字符如*/+/?等之前加上转义符再返回,在需要大量匹配元字符时有那么一点用。
Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。
属性:
方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import
re
m
=
re.match(r
'(\w+) (\w+)(?P
,
'hello world!'
)
print
"m.string:"
, m.string
print
"m.re:"
, m.re
print
"m.pos:"
, m.pos
print
"m.endpos:"
, m.endpos
print
"m.lastindex:"
, m.lastindex
print
"m.lastgroup:"
, m.lastgroup
print
"m.group(1,2):"
, m.group(
1
,
2
)
print
"m.groups():"
, m.groups()
print
"m.groupdict():"
, m.groupdict()
print
"m.start(2):"
, m.start(
2
)
print
"m.end(2):"
, m.end(
2
)
print
"m.span(2):"
, m.span(
2
)
print
r
"m.expand(r'\2 \1\3'):"
, m.expand(r
'\2 \1\3'
)
### output ###
# m.string: hello world!
# m.re: <_sre.SRE_Pattern object at 0x016E1A38>
# m.pos: 0
# m.endpos: 12
# m.lastindex: 3
# m.lastgroup: sign
# m.group(1,2): ('hello', 'world')
# m.groups(): ('hello', 'world', '!')
# m.groupdict(): {'sign': '!'}
# m.start(2): 6
# m.end(2): 11
# m.span(2): (6, 11)
# m.expand(r'\2 \1\3'): world hello!
|
Pattern对象是一个编译好的正则表达式,通过Pattern提供的一系列方法可以对文本进行匹配查找。
Pattern不能直接实例化,必须使用re.compile()进行构造。
Pattern提供了几个可读属性用于获取表达式的相关信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import
re
p
=
re.
compile
(r
'(\w+) (\w+)(?P
, re.DOTALL)
print
"p.pattern:"
, p.pattern
print
"p.flags:"
, p.flags
print
"p.groups:"
, p.groups
print
"p.groupindex:"
, p.groupindex
### output ###
# p.pattern: (\w+) (\w+)(?P
# p.flags: 16
# p.groups: 3
# p.groupindex: {'sign': 3}
|
实例方法[ | re模块方法]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# encoding: UTF-8
import
re
# 将正则表达式编译成Pattern对象
pattern
=
re.
compile
(r
'world'
)
# 使用search()查找匹配的子串,不存在能匹配的子串时将返回None
# 这个例子中使用match()无法成功匹配
match
=
pattern.search(
'hello world!'
)
if
match:
# 使用Match获得分组信息
print
match.group()
### 输出 ###
# world
|
1
2
3
4
5
6
7
|
import
re
p
=
re.
compile
(r
'\d+'
)
print
p.split(
'one1two2three3four4'
)
### output ###
# ['one', 'two', 'three', 'four', '']
|
1
2
3
4
5
6
7
|
import
re
p
=
re.
compile
(r
'\d+'
)
print
p.findall(
'one1two2three3four4'
)
### output ###
# ['1', '2', '3', '4']
|
1
2
3
4
5
6
7
8
|
import
re
p
=
re.
compile
(r
'\d+'
)
for
m
in
p.finditer(
'one1two2three3four4'
):
print
m.group(),
### output ###
# 1 2 3 4
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import
re
p
=
re.
compile
(r
'(\w+) (\w+)'
)
s
=
'i say, hello world!'
print
p.sub(r
'\2 \1'
, s)
def
func(m):
return
m.group(
1
).title()
+
' '
+
m.group(
2
).title()
print
p.sub(func, s)
### output ###
# say i, world hello!
# I Say, Hello World!
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import
re
p
=
re.
compile
(r
'(\w+) (\w+)'
)
s
=
'i say, hello world!'
print
p.subn(r
'\2 \1'
, s)
def
func(m):
return
m.group(
1
).title()
+
' '
+
m.group(
2
).title()
print
p.subn(func, s)
### output ###
# ('say i, world hello!', 2)
# ('I Say, Hello World!', 2)
|
以上就是Python对于正则表达式的支持。熟练掌握正则表达式是每一个程序员必须具备的技能,这年头没有不与字符串打交道的程序了。笔者也处于初级阶段,与君共勉,^_^
另外,图中的特殊构造部分没有举出例子,用到这些的正则表达式是具有一定难度的。有兴趣可以思考一下,如何匹配不是以abc开头的单词,^_^
全文结束
正则表达式的语法:
1.正则表达式简介
正则表达式(Regular expression,简写为Regexes)是一种用来操作和检验字符串数据的强大工具。它相当与一串特殊的字符,用它可以转换成算法,对文本进行匹配等操作。
事实上正则表达式有其自身的一套语法,这种语法对于初学者来说显得有些晦涩难懂。尤其是其构造比较困难,称为很多入门者的障碍。但当掌握后却可以轻易的解决以前不容易解决的很多文本类问题,如验证提取等。
其常用场合有如下三种:
Ø 测试字符串的某个模式。例如,可以对一个输入字符串进行测试,看在该字符串是否存在一个电话号码模式或一个信用卡号码模式。这称为数据有效性验证。
Ø 替换文本。可以在文档中使用一个正则表达式来标识特定文字,然后可以全部将其删除,或者替换为别的文字。
Ø 根据模式匹配从字符串中提取一个子字符串。可以用来在文本或输入字段中查找特定文字。
2.正则表达式基础语法
2.1匹配不同类型的字符
字符类 |
匹配的字符 |
\d |
匹配一个数字字符。等价于 [0-9]。 |
\D |
匹配一个非数字字符。等价于 [^0-9]。 |
\w |
匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。 |
\W |
匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。 |
\s |
匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 |
\S |
匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 |
.(点号) |
任一字符 |
[...] |
括号中的任一字符 |
[^…] |
非括号中的任一字符 |
2.2定位控制字符
^ |
其后的模式必须在字符串的开始处,如果是多行则在任一行的开始C#需要设定Multiline标志 |
$ |
前面的模式必须在一行的末尾,如果是多行,则在任意行的末尾 |
\A |
前面的模式必须在字符串的开始处;多行标志被忽略 |
\z |
前面的模式必须在字符串的末尾处,多行标志被忽略 |
\Z |
前面的模式必须位于字符串的末尾或位于换行符前 |
\b |
匹配一个单词字符的开始,单词字符是[a-zA-Z0-9]中的一位 |
\B |
匹配一个非单词边界的位置, 不在一个单词的开始 |
2.3指定重复字符
{n} |
匹配前面的字符n次 |
{n,} |
匹配前面的字符最少n次 |
{n,m} |
匹配前面的字符n至m次 |
? |
匹配前面的字符0次或1次 |
+ |
匹配前面的字符至少1次 |
* |
匹配前面的字符至少0次 |
2.4特殊控制类
| |
指定字符替换,即该位置可以是|两边的任一个表达式 |
2.5 特殊字符转义序列
\\ |
匹配”\” |
\. |
匹配“.” |
\* |
匹配“*” |
\( |
匹配“(” |
\) |
匹配”)” |
\? |
匹配“?“ |
\+ |
匹配“+“ |
\| |
匹配“|“ |
\{ |
匹配“{“ |
\} |
匹配“}“ |
\^ |
匹配“^“ |
\$ |
匹配“$“ |
\n |
匹配换行符 |
\r |
匹配回车 |
\t |
匹配Tab键 |
\v |
匹配垂直制表符 |
\f |
匹配换页符 |
\nnn |
匹配一个三位八进制数指定的ASCII字符,如\103匹配C |
\xnn |
匹配一个二位16进制数指定的ASCII字符,例如\x43匹配C |
\unnnn |
匹配一个4位16进制数指定的Unicode字符 |
\cV |
匹配一个控制字符,(如复制Ctrl+C) |
2.6正则表达式分组、替换、反向引用等高级应用
以上只是正则表达式的基础部分,从这里开始才算真正开始正则表达式之旅。
² 分组
分组技术可以匹配在一个组中的所有字符,用()来表示,是下面两个技术的基础所在。“()“又称捕获符号。
1. 捕获:()
例子:ABC1EDF2UU
匹配组表达式:([A-Z]{3})\d --匹配3个连续大写字母和一个数字
匹配结果:1.ABC1,2.EDF2
如果用C#中的group,则为ABC,EDF。因为group搜集的是匹配组的内容。
2.非捕获(?:)
使用了非捕获就说明该()中的内容将不作为捕获的组返回,而和其它表达式共同构成匹配项返回。也就是捕获组将不存在。
例:1AF3EDC
匹配表达式:(?:\d|[A-Z])\w --匹配一个数字或字母加一个任意的字符。
匹配结果:1.1A 2.F3 3.ED
没有组被捕获
2. 通过名称捕获(?
定义了名称捕获的组可以在反向匹配中运用名称进行反向引用而不需要再使用数字进行反向捕获。注意组名区分大小写!
² 替换
替换,顾名思义,是将匹配的字符替换成其他指定的字符形式。这个功能是在分组的基础上的(当然或许可以单独存在,但是那样匹配的功能显然不够强大)。在这里有一个技巧是使用附加的匹配字符控制匹配内容。
$group |
用group指定的组号进行替换 |
${name} |
替换由匹配的最后一个子串 |
$$ |
替换字符$ |
$& |
替换整个的匹配 |
$+ |
替换最后捕获的组 |
$ |
替换整个输入的字符串 |
² 反向引用
反向匹配可以引用前面组中的匹配形式。“\匹配组的数字表示(1为基数)“或者”\k
² 高级组
1. 正声明(?=)
规定了括号中的模式必须出现在声明的右侧。模式将不构成匹配的一部分。
2. 负声明(?!)
规定了括号中的模式不能出现在声明的右侧,模式将不构成匹配的一部分。
3. 反向正声明(?<=)
规定了括号中的模式必须出现在声明的左侧,模式将不构成匹配的一部分。
4. 反向负声明(?)
规定了括号中的模式必须出现在声明的左侧。模式不构成匹配的一部分。
5. 非回溯(?>)
防止了正则表达式引擎搜索失败时回溯,这称之为贪婪的子表达式。
如输入字符串:He was very trusting.
正则表达式:.*ing将匹配trusting但是如果加入(?>)ing则不能完成匹配。
非回溯组也是非捕获组。他对于提高正则表达式的效率很有效。
如匹配一个www.****.com的网址。使用www\.(.*)\.com显然要比www\.([^.]*)\.com效率要低的多,因为前者必须使用组中的回溯操作,回溯是很艰难的过程,所以当使用非回溯的正则表达式时时可以显著提高正则表达式的效率的。
其实正则表达式是只注重匹配结果的,所以会努力去匹配所存在的字符串。这就是它的贪婪性所在。(这点其实理解的不是太深)。
注意以上这些(1-5)都不能够用于反向引用,因为以上声明将不作为匹配的一部分。
2.7在正则表达式中做决策
高级决策的两种写法:
1=>(?(expression)yes|no)
2=>(?(?=expression)yes|no)
这两种方式中的的expression匹配则后面进行匹配yes,否则匹配no。
需要注意的一点是yes测试和决策测试是在同一个起点里进行的。
如以下字符串:77-77A 69-AA 57-B
匹配表达式为:(\d7)?-(?(1)\d\d[A-Z]|[A-Z][A-Z])
匹配结果为:
1.77-77A 2. –AA
这个正则表达式中用到了引用组,(?(1)**)中的1也可以换为\1,这样不影响匹配。这个匹配中如果将决策后面的\d\d去掉则会出现不同的结果,这时只会有一个-AA是匹配的。因为决策点和yes表达式是从同一个起点开始匹配所以即使决策点匹配了,但是后面的yes表达式仍然不匹配。就只匹配no部分的表达式。最终结果也必然改变,理解这一点很重要。
2.8 正则表达式的选项
快到结尾了,再说下正则表达式的选项。选项其实就是将正则表达式的设置改到组中来。如(?i:[a-z])将忽略大小写进行匹配。实际上如果学过Javascript中的正则表达式,可以看出这个i在javascript中表示的还是这个意思。
N |
规定只有显示命名的组标号的组才能有效的捕获 |
I |
此选项匹配不区分大小写的匹配 |
X |
此选项规定,非转义的空字符被排除在模式之外,并启用了一个前缀#的注释 |
M |
指定多行模式,修改了^和$的定义 |
S |
指定单行模式 |
2.9正则表达式的规则
1.正则表达式会对输入字符传进行最快的匹配,它一次搜索一个字符,知道实现第一次匹配。
2.发现一个匹配的开始后,正则表达式引擎将继续匹配,直到遇到一个不被模式接收的字符。
3.Regex引擎非常贪婪—只要模式匹配它将匹配尽可能多的字符。
4.Regex渴望实现匹配,所以将在需要时回溯以实现匹配。
5.Regex引擎总是先选择第一个选项。在|式表达式中。
以上的几点很重要。到这正则表达式的几乎所有规则也就讲完了。
最后附一个懒惰匹配常用修饰和其他的限定
*? |
尽可能少地使用重复的第一个匹配 |
+? |
尽可能少地使用重复但至少使用一次 |
?? |
使用零次重复(如有可能)或一次重复 |
{n}? |
等同于{n} |
{n, }? |
尽可能少地使用重复但至少使用n次 |
{n,m}? |
介于n和m之间,尽可能少地使用重复 |
什么是RE?
想必各位大大在做文件查找的时侯都有使用过万用字符”*”,比如说想查找在Windows目录下所有的Word文件时,你可能就会用”*.doc”这样的方式来做查找,因为”*”所代表的是任意的字符。RE所做的就是类似这样的功能,但其功能更为强大。
写程序时,常需要比对字符串是否符合特定样式,RE最主要的功能就是来描述这特定的样式,因此可以将RE视为特定样式的描述式,举个例子来说,”\w+”所代表的就是任何字母与数字所组成的非空字符串(non-null string)。在.NET framework中提供了非常强大的类别库,藉此可以很轻易的使用RE来做文字的查找与取代、对复杂标头的译码及验证文字等工作。
接下来,就让我们来体验一些例子吧。
一些简单的例子
假设要查找文章中Elvis后接有alive的文字符串的话,使用RE可能会经过下列的过程,括号是所下RE的意思:
1. elvis (查找elvis)
上述代表所要查找的字符顺序为elvis。在.NET中可以设定乎略字符的大小写,所以”Elvis”、”ELVIS”或者是”eLvIs”都是符合1所下的RE。但因为这只管字符出现的顺序为elvis,所以pelvis也是符合1所下的RE。可以用2的RE来改进。
2. \belvis\b (将elvis视为一整体的字查找,如elvis、Elvis乎略字符大小写时)
“\b”在RE中有特别的意思,在上述的例子中所指的就是字的边界,所以\belvis\b用\b把elvis的前后边界界定出来,也就是要elvis这个字。
假设要将同一行里elvis后接有alive的文字符串找出来,此时就会用到另外二个特别意义的字符”.”及”*”。”.”所代表就是除了换行字符的任意字符,而”*”所代表的是重复*之前项目直到找到符合RE的字符串。所以”.*”所指的就是除了换行字符外的任意数目的字符数。所以查找同一行里elvis后接有alive的文字符串找出来,则可下如3之RE。
3. \belvis\b.*\balive\b (查找elvis后面