本文译自官方文档:
Regular Expression HOWTO
参考文章:
Python——正则表达式(1)
Python——正则表达式(2)
Python——正则表达式(3)
Python——正则表达式(4)
全文下载:
Python正则表达式基础
======================================================================================
6.常见问题
正则表达式在应用中是非常强大的工具,但有时候它们并不能直观地按照你的意愿来执行。本节内容将会指出一些常见的使用正则表达式的问题。
------------------------------------------------------------------------------------------------------------------------------------------------------
6.1.使用字符串方法
有时候使用re模块就是个错误。如果你要匹配一个固定的字符串,或者一个单独的字符类,并且你没有使用任何re模块的标志,比如IGNORECASE,那么你就并不需要正则表达式了。Python的字符串有一些操作固定字符串的方法,通常它们速度更快,因为它们都是通过单独的经过优化的的C语言小循环实现的,而不是通过更加通用的正则表达式引擎。
举一个替换固定字符串的例子。例如,你想使用deed替换word。re.sub()似乎可以做这件事,但是还是直接使用字符串的replace()方法把。但要注意replace()方法也会替换单词中间的word字符串,比如会把swordfish替换变成sdeedfish,而且简单的正则表达式word也会这么做。要避免替换单词中间的word子串,必须使用正则表达式模式 \bword\b,以此来确定单词两边存在单词边界。这就必须使用正则表达式,因为这超出了replace()函数的功能范畴。
另一个常见的任务是从一个字符串中删除一个单独的字符或者将它替换成另一个字符。你可能会通过re.sub(‘\n’,’ ’,S)这样的方式去做,但是字符串的translata()方法完全可以做同样的工作并且通常比正则表达式运行地更快。
总之,在你使用re模块之前,先要考虑你的问题是否可以通过更快更简单的字符串函数来解决。
------------------------------------------------------------------------------------------------------------------------------------------------------
6.2.match() VS search()
match()方法只在字符串的开始处匹配,而search()方法会扫描整个字符串检查是否存在一个匹配的字符串。记住这个区别非常重要。再次强调,match()方法只报告一次开始位置为0的匹配,如果匹配的开始位置不为0,match()方法不会报告它。
>>> print(re.match('super','superstition').span()) (0, 5) >>> print(re.match('super','insuperable')) None而search()方法会扫描整个字符串,然后报告它找到的第一次匹配:
>>> print(re.search('super','superstition').span()) (0, 5) >>> print(re.search('super','insuperable').span()) (2, 7)有时你可能会冒险使用re.match(),并且在RE的前面加上‘.*’,最好不要这么做,而是去使用re.search()。正则表达式引擎会对正则表达式做一些分析,从而加快执行匹配的速度。一般分析会先找到第一个匹配的字符是什么,比如,一个以Crow开头的模式必定从字符C开始匹配,那么匹配引擎在分析后会快速扫描字符串去找到字符C,在字符C找到后才开始全部匹配。
6.3.贪婪VS非贪婪
当重复一个正则表达式,比如 a*,结果就是尽可能多地去匹配。这个结果有时候会让你很受伤,尤其是在你匹配一对分隔符的时候。比如,由尖括号括起来的HTML标签。由于星号重复的贪婪本性,常规的正则表达式去匹配一对单个的HTML标签会出错。
>>> s = '<html><head><title>Title</title>' >>> len(s) 32 >>> print(re.match('<.*>',s).span()) (0, 32) >>> print(re.match('<.*>',s).group()) <html><head><title>Title</title>正则表达式先匹配<html>中的左括号 < ,然后 .* 贪婪地匹配剩下的所有字符,这时候正则表达式中的右括号 > 在字符串结尾处无法匹配,所以匹配引擎回退一个字符,直到它找到了匹配最后一个右括号 > 。最后匹配的结果就是从<html>中的左括号到</title>中右括号之间的内容,但这并不是你想要的结果。
>>> print(re.match('<.*?>',s).group()) <html>注意:使用正则表达式分析HTML或者XML是很痛苦的。你编写的正则表达式可以处理一些常见的情况,但是HTML和XML总是会打破规则。所以当你编写一个正则表达式去处理所有可能的情况时,这个正则表达式会非常复杂。像这样的话,还是建议使用 HTML 和 XML 解析器来处理更合适。
------------------------------------------------------------------------------------------------------------------------------------------------------
6.4.使用re.VERBOSE
现在你已经注意到了,正则表达式是一个紧凑的记法,但是它们会非常难于阅读。适度复杂的正则表达式就会包含很多反斜杠、括号和元字符等,这会让它们难于阅读和理解。
对于这样的正则表达式,编译的时候指定re.VERBOSE标志就会很有帮助,因为它允许你更加清晰地组织正则表达式。
re.VERBOSE标志有几个效果。正则表达式中的空格如果不在字符类中就会被忽略,这就意味着类似 dog | cat 这样的正则表达式和可读性较差的dog|cat意义相同,但是字符类 [a b] 还是会匹配字符a、b或者一个空格。另外,你也可以在正则表达式中写注释,注释是从#号到下一行之间的内容。当使用三引号字符串时,会让正则表达式变得更加简洁。
pat = re.compile(r''' \s* #Skip leading whitespace (?P<header>[^:]+) #Header name \s* : #Whitespace,and a colon (?P<value>.*?) #The header's value —— *? used to #lose the following trailing whitespace \s*$ #Trailing whitespace to end-of-line ''',re.VERBOSE)这比下面写的要清晰多了:
pat = re.compile(r'\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$')