为了方便阅读,省略英文.
另外一个重复匹配字符是+,可以匹配一个或者是多个字符。请注意+和*的区别,*是匹配零个和多个,+是匹配一个或者是多个。来看个例子吧,ca+t可以匹配cat(1个a),caaat(3个a),但是不能匹配ct,因为ct中缺少a.
还有两个重复匹配的限定符,一个是问号,?,可以匹配一次或者零次;?一般用来表示某些字符时可选的。比如在英语中分行符-, home-?brew 可以匹配没有分行符的单词 homebrew 或者具有分行符的单词 home-brew.
另外一个重复匹配限定符就是{m,n}, 这是是一个最灵活的限定符,上面讲的一切的限定符都可以使用这个通用的重复限定符来表达。在这个表达式中, m 和n都是十进制。这个表达式意思是,重复至少为m次,至多为n次。举个例子来说, a/{1,3}b 可以匹配 a/b, a//b, 以及 a///b. 但是不会匹配 ab, 原因是其没有包含/字符, 他也不能匹配 ab, 这个是因为含有四个/。
在这个强大的表达式中,我们可以生类m和n。这样的话,我们会使用缺省值来代替我们省略的m或者。假使我们省略m的话我们会使用0来代替。如果省略了n,我们会使用语言的int类型的上限,也就是2^31,这个值近似于无限。
喜欢偷懒的人也许注意到了,其实*+?都可以使用{m,n}来表示。我们可以将其看成是{m,n}的偷懒的方式。{0,} 跟 *是一样的。{1,} 跟 +是一样的, {0,1} 跟 ?是一样的。鼓励大家使用偷懒的方式 *, +, 或者 ?,原因是这些字符易读,还有一个原因是引擎对这些字符做了优化。
使用正规则表达式
现在我们来看一些简单的正则表达式。我们首先想到的是我们如何使用正则表达式?不用担心,re模块已经帮我们实现了一个python 的正则表达式引擎。Re模块为了速度是使用C语言编写的。所以使用正则表达式比使用普通的字符串操作可以得到更好的效率。为了更加进一步的提高效率,我们可以将模式串编译成二进制结构,然后再使用正则匹配。当你要匹配很多次的时候,这个还是挺必要的。
编译模式串
被编译的模式串可以用来匹配或者用来进行模式替换。
>>>
>>> importre
>>> p = re.compile('ab*')
>>> p
<_sre.SRE_Pattern object at 0x...>
re.compile()也可以接受 flags 参数, 用来使能各种特殊的功能或者是使能各种语法变体。我们在稍后的文章中会一一介绍。现在我们先来看简单的例子。
>>> p = re.compile('ab*', re.IGNORECASE)
传入re.compile()的RE参数是一个标准的字符串. 正则表达式的模式表达式不是python 的核心部分,他们也没有特殊的语法,所以RE只能以标准字符串的形式传入. (有些应用根本就不会使用正则表达式,所以我们也没有必要将其纳入语言的核心) 为了将RE引入到python中,我们采用了跟socket 和 zlib一样的策略,将其作为模块引入。
将模式使用字符串表示保持了python简洁的一贯风格,但是也带来了很多的负面影响。我们会在下一个章节中稍作介绍。
都是反斜杠惹的祸
前面已经提到过,正则表达式使用('\')来制定一些特殊的形式或者让一些特殊的匹配字符恢复他们的本来面貌。这个可能会个python中有些字符串的真实字面值有冲突。(挺绕口的,看例子吧)
我们会在LaTeX中经常使用\section,作为起始,来表示我们在编写的代码中写了什么东西。假使你要写一个模式来匹配 \section, 你必须要使用反斜杠来解除反斜杠或者其他特殊字符的特殊功能。所以我们会把模式写成 \\section. 我们最终要传给re.compile()的一定是 \\section. 不要忘记,python同样使用反斜杠,所以我们必须再次使用反斜杠来解除我们传递给re.compile()的两个反斜杠。看具体的实现吧。
Characters | Stage |
\section | 我们匹配的字串 |
\\section | re.compile()要使用的串 |
"\\\\section" | 传递给re.compile()的串 |
简而言之,为了匹配一个反斜杠我们必须使用四个反斜杠'\\\\' ,真正的串中需要两个,为了构造这两个反斜杠,我们必须继续使用反斜杠。所以这几造成了反斜杠风暴. 在正则表达式中重复的使用反斜杠,会造成反斜杠风暴,导致串极难读懂。(我来试着解释一下原因,没有经过验证,纯属猜测:RE模块本身使用C语言编写,C语言中同样会使用两个反斜杠来表示一个反斜杠,这个挺容易理解的。我们难于理解的是为什么还在需要两个呢?原因在于python的内置的字符串类型给C的char*不能通用,这里面存在一次转换,在python将串传递给C的我们必须构造一个C的字符串。)
一种解决方案是是使用python 的内置的原生串来表示正则表达式。以'r'开头的字符串反斜杠不会做任何处理,比如 r"\n" 是两个字符 '\' 和 'n', 而 "\n" 表示一个换行符.在python中正则表达式我们强烈建议使用原生串书写.
Regular String | Raw string |
"ab*" | r"ab*" |
"\\\\section" | r"\\section" |
"\\w+\\s+\\1" | r"\w+\s+\1" |