Python 正则表达式之三:贪婪

dgvk5778 8年前
   <p>默认情况下,正则表达式将进行贪婪匹配。所谓“ <strong>贪婪</strong> ”,其实就是在多种长度的匹配字符串中,选择较长的那一个。例如,如下正则表达式本意是选出人物所说的话,但是却由于“ <strong>贪婪</strong> ”特性,出现了匹配不当:</p>    <pre>  <code class="language-python">>>> sentence = """You said "why?" and I say "I don't know"."""  >>> re.findall(r'"(.*)"', sentence)  ['why?" and I say "I don\'t know']  </code></pre>    <p>再比如,如下的几个例子都说明了正则表达式“ <strong>贪婪</strong> ”的特性:</p>    <pre>  <code class="language-python">>>> re.findall('hi*', 'hiiiii')  ['hiiiii']  >>> re.findall('hi{2,}', 'hiiiii')  ['hiiiii']  >>> re.findall('hi{1,3}', 'hiiiii')  ['hiii']  </code></pre>    <h2>非贪婪</h2>    <p>当我们期望正则表达式“ <strong>非贪婪</strong> ”地进行匹配时,需要通过语法明确说明:</p>    <p>{2,5}? 捕获2-5次,但是优先次数少的匹配</p>    <p>在这里,问号 <strong>?</strong> 可能会有些让人犯晕,因为之前他已经有了自己的含义:前面的匹配出现0次或1次。其实,只要记住,当问号出现在表现 <strong>不定次数</strong> 的正则表达式部分之后时,就表示非贪婪匹配。</p>    <p>还是上面的那几个例子,用非贪婪匹配,则结果如下:</p>    <pre>  <code class="language-python">>>> re.findall('hi*?', 'hiiiii')  ['h']  >>> re.findall('hi{2,}?', 'hiiiii')  ['hii']  >>> re.findall('hi{1,3}?', 'hiiiii')  ['hi']  </code></pre>    <p>另外一个例子中,使用非贪婪匹配,结果如下:</p>    <pre>  <code class="language-python">>>> sentence = """You said "why?" and I say "I don't know"."""  >>> re.findall(r'"(.*?)"', sentence)  ['why?', "I don't know"]  </code></pre>    <h2>捕获与非贪婪</h2>    <p>严格来说,这一部分并不是非贪婪特性。但是由于其行为与非贪婪类似,所以为了方便记忆,就将其放在一起了。</p>    <p>(?=abc) 捕获,但不消耗字符,且匹配abc</p>    <p>(?!abc) 捕获,不消耗,且不匹配abc</p>    <p>在正则表达式匹配的过程中,其实存在“消耗字符”的过程,也就是说,一旦一个字符在匹配过程中被检索(消耗)过,后面的匹配就不会再检索这一字符了。</p>    <p>知道这个特性有什么用呢?还是用例子说明。比如,我们想找出字符串中出现过1次以上的单词:</p>    <pre>  <code class="language-python">>>> sentence = "Oh what a day, what a lovely day!"  >>> re.findall(r'\b(\w+)\b.*\b\1\b', sentence)  ['what']  </code></pre>    <p>这样的正则表达式显然无法完成任务。为什么呢?原因就是,在第一个(\w+)匹配到what,并且其后的\1也匹配到第二个what的时候,“Oh what a day, what”这一段子串都已经被正则表达式消耗了,所以之后的匹配,将直接从第二个what之后开始。自然地,这里只能找出一个出现了两次的单词。</p>    <p>那么解决方案,就和上面提到的(?=abc)语法相关了。这样的语法可以在分组匹配的同时,不消耗字符串!所以,正确的书写方式应该是:</p>    <pre>  <code class="language-python">>>> re.findall(r'\b(\w+)\b(?=.*\b\1\b)', sentence)  ['what', 'a', 'day']  </code></pre>    <p>如果我们需要匹配一个至少包含两个不同字母的单词,则可以使用(?!abc)的语法:</p>    <pre>  <code class="language-python">>>> re.search(r'([a-z]).*(?!\1)[a-z]', 'aa', re.IGNORECASE)  >>> re.search(r'([a-z]).*(?!\1)[a-z]', 'ab', re.IGNORECASE)  <_sre.SRE_Match object; span=(0, 2), match='ab'>  </code></pre>    <p>Python正则表达式中关于贪婪的内容至此就总结完毕了。下一篇文章,我会继续总结一下Python正则表达式re模块的一些API的用法。</p>    <p> </p>    <p> </p>    <p>来自:http://blog.guoyb.com/2017/03/01/python-regex-3/</p>    <p> </p>