本文译自官方文档:Regular Expression HOWTO
参考文章:Python——正则表达式(1)
全文下载 :Python正则表达式基础
======================================================================================
3.使用正则表达式
现在,我们已经学习了一些简单的正则表达式,但我们应该怎么在Python中使用它们呢?re模块提供了一个连接正则表达式引擎的接口,允许你将RE编译成对象并利用它们进行匹配。
------------------------------------------------------------------------------------------------------------------------------------------------------
3.1.编译正则表达式正则表达式被编译成模式对象,该对象拥有很多方法来进行各种各样的操作,比如按照模式匹配查找或者执行字符串的替换。
>>> import re >>> p = re.compile('ab*') >>> p re.compile('ab*')re.compile()方法也可以接受一个可选flags参数,用于指定各种特殊功能和语法变更,我们将会在之后一一学习。现在我们看一个简单的例子:
>>> p = re.compile('ab*',re.IGNORECASE)正则表达式作为一个字符串参数传递给re.compile()方法。由于正则表达式并不是Python的核心部分,也没有特殊的语法去表示它们,所以只能被作为字符串处理(有许多应用并不需要RE,所以没有必要把RE纳入Python的核心部分),相反,正则表达式re模块仅作为C的扩展模块嵌入的Python中,就像socket或者zlib模块。
把正则表达式作为字符串也让Python更加简洁,但是也有一个缺点,下边我们就来谈一谈。
------------------------------------------------------------------------------------------------------------------------------------------------------
3.2.麻烦的反斜杠
就像上文描述的,正则表达式使用反斜杠 \ 来使一些字符拥有特殊的意义(比如\s)或者去掉特殊字符的特殊意义(比如\*就是表示星号而没有特殊的意义),这会与Python字符串中实现相同功能的字符发生冲突。
假如我们要写一个RE来匹配LaTeX文件中的一个字符串“\section”,首先你要先在程序代码中写出将要匹配的字符串。接着,你需要在反斜杠以及其他元字符前面加上反斜杠以去掉它们的特殊含义,所以得到结果“\\section”,这个字符串将传递给re.compile()函数。然而,要知道Python字符串中的反斜杠也有特殊意义,所以要再次在两个反斜杠前面加上反斜杠,得到字符串“\\\\section”。
匹配字符串 |
匹配步骤 |
\section | 将要匹配的字符串 |
\\section | 正则表达式中用‘\\’表示‘\’ |
“\\\\section” | Python字符串中也用‘\\’表示‘\’ |
解决方法是使用Python中的原始字符串。所谓原始字符串,即在字符串最前面加上字母r,这样字符串中的反斜杠都会被去掉特殊语义,看做普通字符。比如,字符串 r”\n” 是包含‘\’和‘n’两个字符的字符串,而字符串 “\n” 是只有一个换行符的字符串。正则表达式通常使用Python中的原始字符串来表示。
正则表达式字符串 |
原始字符串 |
“ab*” | r”ab*” |
“\\\\section” | r”\\section” |
“\\w+\\s+\\1” | r”\w+\s+\1” |
3.3.执行匹配
将正则表达式编译之后会得到一个模式对象,那么你会用它做什么呢?模式对象包含许多方法和属性,我们在这里只介绍最常用的几个,你可以通过查看re模块的文档来查看完整的列表。
方法/属性 |
功能 |
match() | 判断一个正则表达式是否从开始处匹配一个字符串 |
search() | 扫描一个字符串,找到正则表达式匹配的第一个位置 |
findall() | 扫描一个字符串,找到匹配正则表达式的所有子字符串,并将它们以列表的形式返回 |
finditer() | 扫描一个字符串,找到匹配正则表达式的所有子字符串,并将它们以迭代器的形式返回 |
你可以在交互模式下使用re模块来学习这些内容。如果你能够使用tkinter,你可以看一下Tools/demo/redemo.py这个程序,这是随着Python发布的一个示例程序。它可以让你输入正则表达式和字符串,并输出两者是否匹配。当你测试一个复杂的正则表达式的时候,redemo.py是非常有用的。Phil Schwartz’s Kodos也是一个开发和测试正则表达式的一个很有用的交互式工具。
我们使用标准Python解释器来解释这些例子。首先,打开Python解释器,导入re模块,然后编译一个RE:
>>> import re >>> p = re.compile('[a-z]+') >>> p re.compile('[a-z]+')现在你可以利用正则表达式 [a-z]+ 来匹配各种字符串。但是一个空的字符串并不能被匹配,因为加号 + 表示重复1次以上,在这种情况下,match()方法将会返回None。另外,这个结果在解释器中不会输出,不过你可以明确地调用print()方法来输出这个结果。
>>> p.match('') >>> print(p.match('')) None接着,让我们尝试一个它可以匹配的字符串,比如字符串“tempo”。这种情况下,match()方法将会返回一个匹配对象(match object),为了之后使用这个对象,你应该把这个结果保存在一个变量中。
>>> m = p.match('tempo') >>> m <_sre.SRE_Match object; span=(0, 5), match='tempo'>
现在你可以利用匹配对象查询匹配字符串的信息。匹配对象实例也有一些方法和属性,这里列出最重要的几个:
方法/属性 | 功能 |
group() | 返回匹配的字符串 |
start() | 返回字符串匹配的开始位置 |
end() | 返回字符串匹配的结束位置 |
span() | 返回一个元祖表示匹配位置,(开始,结束) |
>>> m.group() 'tempo' >>> m.start(),m.end() (0, 5) >>> m.span() (0, 5)group()方法返回由RE.start()和RE.end()位置确定的子字符串。span()方法用一个元祖返回子字符串的开始位置和结束位置。但要注意的是,match()方法是判断正则表达式是否从开始处匹配字符串,所以start()方法总是返回0。然而,search()方法就不一样了,它扫描整个字符串,匹配的子字符串的开始位置不一定是0。
>>> print(p.match(':::message')) None >>> m = p.search(':::message') >>> print(m) <_sre.SRE_Match object; span=(3, 10), match='message'> >>> m.group() 'message' >>> m.span() (3, 10)在实际的程序中,最常用的写法是将匹配对象存储在一个变量中,然后检查它是否为None。就像下边这样:
p = re.compile(…) m = p.match(‘string goes here’) if m: print(‘Match found: ’ , m.group()) else: print(‘No match’)有两个方法可以返回所有匹配的子字符串。findall()方法返回所有匹配字符串的列表:
>>> p = re.compile('\d+') >>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping') ['12', '11', '10']findall()方法需要在它返回结果之前创建出整个列表。但是finditer()方法将匹配对象作为迭代器返回(译者注:迭代器的方式更节省内存,效率更高)。
>>> iterator = p.finditer('12 drummers drumming, 11 pipers piping, 10 lords a-leaping') >>> iterator <callable_iterator object at 0x036A2110> >>> for match in iterator: print(match.span()) (0, 2) (22, 24) (40, 42)------------------------- ------------------------- ------------------------- ------------------------- ------------------------- -------------------------
>>> print(re.match(r'From\s+','Fromage amk')) None >>> print(re.match(r'From\S+','Fromage amk')) <_sre.SRE_Match object; span=(0, 7), match='Fromage'> >>> print(re.match(r'From\S+','Fromage amk').group()) Fromage >>> re.match(r'From\s+','From amk Thu May 14 19:12:10 1998') <_sre.SRE_Match object; span=(0, 5), match='From '>其实,这些函数只是简单地为你创建了一个模式对象,并且可以调用其相关函数。另外,它将编译好的模式对象存放在缓存中,所以如果之后你使用了相同的正则表达式,就不用了再次创建模式了,可以实现快速调用。
编译标志 |
含义 |
ASCII,A | 使得转义符号如\w,\b,\s和\d只匹配ASCII字符 |
DOTALL,S | 使得点号 . 可以匹配任何符号,包括换行符 |
IGNORECASE,I | 匹配不区分大小写 |
LOCALE,L | 支持当前的语言(区域)设置 |
MULTILINE,M | 多行匹配,会影响^和$ |
VERBOSE,X(for ‘extended’) | 启用详细的正则表达式 |
>>> charref = re.compile(r''' &[#] #开始数字引用 ( 0[0-7]+ #八进制格式 |[0-9]+ #十进制格式 |x[0-9a-fA-F]+ #十六进制格式 ) ; #结尾分号 ''',re.VERBOSE)如果不用VERBOSE设置,这个正则表达式将会是下面这个格式:
>>> charref = re.compile('&[#](0[0-7]+' '|[0-9]+' '|x[0-9a-fA-F]+);')在上述的例子中,我们使用了Python自动串联字符串的功能,从而将正则表达式分成了几个更小的部分,但是它依旧没有使用re.VERBOSE版本的RE好理解。