天天看點

第十六章 Python正規表達式

正規表達式在每種語言中都會有,目的就是比對符合你預期要求的字元串。

Python正規表達式主要由re庫提供,擁有了基本所有的表達式。

16.1 Python正規表達式

符号

描述

示例

.

比對除換行符(\n)之外的任意單個字元

字元串123\n456,比對123:1.3

^

比對字元串開頭

abc\nxyz,比對以abc開頭的行:^abc

$

比對字元串結尾

abc\nxyz,比對以xyz結束的行:xyz$

*

比對多個

hello\nword,比對以w開頭d結尾的單詞:w*d

+

比對1個或多個

abc\nabcc\nadf,比對abc和abcc:ab+

比對0個或1個

abc\nac\nadd,比對abc或ac:a?c

[.]

比對中括号之中的任意一個字元

abcd\nadd\nbbb,比對abcd和add:[abc]

[ .-.]

比對中括号中範圍内的任意一個字元

abcd\nadd\nbbb,比對abcd和add:[a-c]

[^]

比對[^字元]之外的任意一個字元

abc\n\abb\nddd,不比對abc和abb:[^a-c]

{n}或{n,}

比對花括号前面字元至少n個字元

1\n\12\n123\n1234,比對123和1234:[0-9]{3}

{n,m}

比對花括号前面字元至少n個字元,最多m個字元

1\n\12\n123\n1234\n12345,比對123和1234 :[0-9]{3,4}

|

比對豎杠兩邊的任意一個

abc\nabd\abe,比對abc和abd:ab(c|d)

\

轉義符,将特殊符号轉成原有意義

1.2,比對1.2:1\.2,否則112也會比對到

特殊字元

\A

比對字元串開始

與^差別是:當使用修飾符re.M比對多行時,\A将所有字元串作為一整行處理。

abc123\nabc456,比對abc123:\Aabc,^則都會比對到

\Z

比對字元串結束

與\A同理

\b

比對字元串開始或結束(邊界)

abc\nabcd,比對a開頭并且c結尾字元串:\babc\b

\B

與\b相反

\d

比對任意十進制數,等效[0-9]

1\n123\nabc,比對1和123:[0-9],包含單個數字的都會比對到,如果隻想比對1:\b[0-9]\b

\D

比對任意非數字字元,等效[^0-9]

1\n12\nabc,比對abc:[^0-9]

\s

比對任意空白字元,等效[\t\n\r\f\v]

1\n a,注意a前面有個空格,比對a:\s

\S

比對任意非空白字元,等效[^\t\n\r\f\v]

1\n a\n ,比對1和a:\S

\w

比對任意數字和字母,等效[a-zA-Z0-9_]

1\n a\n ,比對1和a:\w

\W

與\w相反,等效[^a-zA-Z0-9_]

\n

反向引用,n是數字,從1開始編号,表示引用第n個分組比對的内容

ff,比對ff:(.)\1,即"ff"

擴充正規表達式

( )

比對小括号中正規表達式或字元。用上面\n特殊字元引用。

(?#...)

注釋小括号内的内容

(?:...)

不儲存比對的分組

(?P<name>...)

命名分組,name是辨別名稱,預設是數字ID辨別分組比對

(?=...)

比對後面能比對表的達式...,稱為正先行斷言

(?!...)

比對後面不能比對的表達式...,稱為負先行斷言

(?<=...)

比對前面能比對的表達式...,稱為正後發斷言

(?<!...)

比對前面不能比對的表達式...,稱為負後發斷言

(?(id/name)Y/N)

如果分組提供的id或name存在,則使用Y表達式比對,否則N表達式比對

斷言:斷言就是一個條件,判斷某個字元串前面或後面是否滿足某種規律的字元串,不能引用。

16.2 re庫

re子產品有以下常用的方法:

方法

re.compile(pattern, flags=0)

把正規表達式編譯成一個對象

re.findall(pattern, string, flags=0)

以清單形式傳回所有比對的字元串

re.finditer(pattern, string, flags=0)

以疊代器形式傳回所有比對的字元串

re.match(pattern, string, flags=0)

比對字元串開始,如果不比對傳回None

re.search(pattern, string, flags=0)

掃描字元串尋找比對,如果符合傳回一個比對對象并終止比對,否則傳回None

re.split(pattern, string, maxsplit=0, flags=0)

以比對模式作為分隔符,切分字元串為清單

re.sub(pattern, repl, string, count=0, flags=0)

字元串替換,repl替換比對的字元串,repl可以是一個函數

re.purge()

清除正規表達式緩存

參數說明:

pattern   正規表達式

string    要比對的字元串

flags     标志位的修飾符,用于控制表達式比對模式

标志位的修飾符,有以下可選項:

修飾符

re.DEBUG

顯示關于編譯正則的debug資訊

re.I/re.IGNORECASE

忽略大小寫

re.L/re.LOCALE

本地化比對,影響\w,\w,\b,\B,\s和\S

re.M/re.MULTILINE

多行比對,影響^和$

re.S/re.DOTAIL

比對所有字元,包括換行符\n,如果沒這個标志将比對除了換行符

re.U/re.UNICODE

根據unicode字元集解析字元。影響影響\w,\w,\b,\B,\d,\D,\s和\S

re.X/re.VERBOSE

允許編寫更好看、更可讀的正規表達式,也可以在表達式添加注釋,下面會講到

部落格位址:http://lizhenliang.blog.51cto.com

QQ群:323779636(Shell/Python運維開發群)

16.2.1 re.compile()

把正規表達式編譯成一個對象,友善再次調用:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<code>&gt;&gt;&gt; </code><code>import</code> <code>re</code>

<code>prog </code><code>=</code> <code>re.</code><code>compile</code><code>(pattern)</code>

<code>result </code><code>=</code> <code>prog.match(string)</code>

<code>等效于</code>

<code>result </code><code>=</code> <code>re.match(pattern, string)</code>

<code>例如:檢查字元串是否比對</code>

<code>&gt;&gt;&gt; </code><code>def</code> <code>displaymatch(match):</code>

<code>...     </code><code>if</code> <code>match </code><code>is</code> <code>None</code><code>:</code>

<code>...         </code><code>return</code> <code>None</code>

<code>...     </code><code>return</code> <code>'&lt;Match: %r, group=%r&gt;'</code> <code>%</code> <code>(match.group(), match.groups())</code>

<code>...</code>

<code>&gt;&gt;&gt; valid </code><code>=</code> <code>re.</code><code>compile</code><code>(r</code><code>"^[a-c1-3]{3}$"</code><code>)</code>

<code>&gt;&gt;&gt; displaymatch(valid.match(</code><code>"a1b"</code><code>))   </code><code># 可用</code>

<code>"&lt;Match: 'a1b', group=()&gt;"</code>

<code>&gt;&gt;&gt; displaymatch(valid.match(</code><code>"a1b2"</code><code>))  </code><code># 不可用</code>

<code>&gt;&gt;&gt; displaymatch(valid.match(</code><code>"bbb"</code><code>))   </code><code># 可用</code>

<code>"&lt;Match: 'bbb', group=()&gt;"</code>

16.2.1 match()

<code>例如:判斷字元串開頭是否比對字元</code>

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(r</code><code>'hello'</code><code>, </code><code>'hello world'</code><code>)           </code>

<code>&gt;&gt;&gt; </code><code>print</code> <code>m  </code><code># 比對到字元串開頭是hello</code>

<code>&lt;_sre.SRE_Match </code><code>object</code> <code>at </code><code>0x7f56d5634030</code><code>&gt;</code>

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(r</code><code>'world'</code><code>, </code><code>'hello world'</code><code>)     </code>

<code>&gt;&gt;&gt; </code><code>print</code> <code>m  </code><code># 沒有比對到</code>

<code>None</code>

正則對象比對方法:

1)group([group1, ...])

18

19

20

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(r</code><code>'(\w+) (\w+)'</code><code>, </code><code>'hello world'</code><code>)    </code>

<code>&gt;&gt;&gt; m.group(</code><code>0</code><code>)    </code><code># 全部組比對</code>

<code>'hello world'</code>

<code>&gt;&gt;&gt; m.group(</code><code>1</code><code>)    </code><code># 第一個括号子組</code>

<code>'hello'</code>

<code>&gt;&gt;&gt; m.group(</code><code>2</code><code>)    </code><code># 第二個括号子組</code>

<code>'world'</code>

<code>&gt;&gt;&gt; m.group(</code><code>1</code><code>, </code><code>2</code><code>) </code><code># 多個參數傳回一個元組</code>

<code>(</code><code>'hello'</code><code>, </code><code>'world'</code><code>)</code>

<code>通過分子重命名的名字來引用分組結果:</code>

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(r</code><code>'(?P&lt;first_name&gt;\w+) (?P&lt;last_name&gt;\w+)'</code><code>, </code><code>'hello world'</code><code>)    </code>

<code>&gt;&gt;&gt; m.group(</code><code>'first_name'</code><code>)</code>

<code>&gt;&gt;&gt; m.group(</code><code>'last_name'</code><code>)</code>

<code># 命名組也可以引用他們的索引</code>

<code>&gt;&gt;&gt; m.group(</code><code>1</code><code>)</code>

<code>&gt;&gt;&gt; m.group(</code><code>2</code><code>)</code>

如果一組比對多次,隻有最後一個比對:

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(r</code><code>"(..)+"</code><code>, </code><code>"a1b2c3"</code><code>)    </code>

<code>'c3'</code>

2)groups([default])

傳回一個元組包含所有子組的比對。

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(r</code><code>"(\d+)\.(\d+)"</code><code>, </code><code>"24.1632"</code><code>)    </code>

<code>&gt;&gt;&gt; m.groups()</code>

<code>(</code><code>'24'</code><code>, </code><code>'1632'</code><code>)</code>

3)groupdict([default])

傳回子組名字作為鍵,比對結果作為值的字典。

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(r</code><code>"(?P&lt;first_name&gt;\w+) (?P&lt;last_name&gt;\w+)"</code><code>, </code><code>"hello world"</code><code>)    </code>

<code>&gt;&gt;&gt; m.groupdict()</code>

<code>{</code><code>'first_name'</code><code>: </code><code>'hello'</code><code>, </code><code>'last_name'</code><code>: </code><code>'world'</code><code>}</code>

4)start()和end()

例如:去掉郵件位址的某字元

<code>&gt;&gt;&gt; email </code><code>=</code> <code>"tony@163_126.com"</code>    

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.search(r</code><code>"_126"</code><code>, email)</code>

<code>&gt;&gt;&gt; email[:m.start()] </code><code>+</code> <code>email[m.end():]</code>

<code>'[email protected]'</code>

5)span()

以清單形式傳回比對索引開始和結束值:

<code>&gt;&gt;&gt; m.span()</code>

<code>(</code><code>8</code><code>, </code><code>12</code><code>)</code>

6)pos和endpos

傳回字元串開始和結束索引值:

<code>&gt;&gt;&gt; m.pos</code>

<code>0</code>

<code>&gt;&gt;&gt; m.endpos</code>

<code>16</code>

16.2.3 search()

search()方法也具備match()方法的正則對象比對方法,差別是search()比對到第一個後就傳回并終止比對。

例如:比對第一個結果就傳回

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.search(r</code><code>"c"</code><code>, </code><code>"abcdefc"</code><code>)</code>

<code>&gt;&gt;&gt; m.group()</code>

<code>'c'</code>

<code>(</code><code>2</code><code>, </code><code>3</code><code>)</code>

16.2.4 split()

例如:以數字作為分隔符拆分字元串

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.split(r</code><code>"\d+"</code><code>, </code><code>"a1b2c3"</code><code>)       </code>

<code>&gt;&gt;&gt; m</code>

<code>[</code><code>'a'</code><code>, </code><code>'b'</code><code>, </code><code>'c'</code><code>, '']</code>

<code>16.2</code><code>.</code><code>4</code> <code>sub()</code>

例如:替換2016

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.sub(r</code><code>"\d+"</code><code>, </code><code>"2017"</code><code>, </code><code>"the year 2016"</code><code>)</code>

<code>'the year 2017'</code>

例如:repl作為一個函數

<code>&gt;&gt;&gt; </code><code>def</code> <code>repl(m):                         </code>

<code>...   </code><code>return</code> <code>str</code><code>(</code><code>int</code><code>(m.group(</code><code>'v'</code><code>)) </code><code>*</code> <code>2</code><code>)     </code>

<code>&gt;&gt;&gt; re.sub(r</code><code>'(?P&lt;v&gt;\d+)'</code><code>, repl, </code><code>"123abc"</code><code>)</code>

<code>'246abc'</code>

函數傳回必須是一個字元串。

16.2.5 findall()和finditer()

例如:得到所有比對的數字

<code>&gt;&gt;&gt; text </code><code>=</code> <code>"a1b2c3"</code>

<code>&gt;&gt;&gt; re.findall(r</code><code>'\d+'</code><code>, text)</code>

<code>[</code><code>'1'</code><code>, </code><code>'2'</code><code>, </code><code>'3'</code><code>]</code>

<code>&gt;&gt;&gt; </code><code>for</code> <code>m </code><code>in</code> <code>re.finditer(r</code><code>'\d+'</code><code>, text):</code>

<code>...   </code><code>print</code> <code>m.group()</code>

<code>1</code>

<code>2</code>

<code>3</code>

16.2.6 原始字元串符号"r"

上面所看到的(r"\d+")其中的r代表原始字元串,沒有它,每個反斜杠'\'都必須再加一個反斜杠來轉義它。

例如,下面兩行代碼功能上是相同的:

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(r</code><code>"\W(.)\1\W"</code><code>, </code><code>" ff "</code><code>)</code>

<code>' ff '</code>

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(</code><code>"\\W(.)\\1\\W"</code><code>, </code><code>" ff "</code><code>)</code>

<code>&gt;&gt;&gt; m </code><code>=</code> <code>re.match(</code><code>"\W(.)\1\W"</code><code>, </code><code>" ff "</code><code>)   </code>

<code>Traceback (most recent call last):</code>

<code>  </code><code>File</code> <code>"&lt;stdin&gt;"</code><code>, line </code><code>1</code><code>, </code><code>in</code> <code>&lt;module&gt;</code>

<code>AttributeError: </code><code>'NoneType'</code> <code>object</code> <code>has no attribute </code><code>'group'</code>

\W比對第一個和最後一個空字元,(.)比對第一個f,\1引用前面(.)比對的結果(還是f),即是r"ff"

16.3 貪婪和非貪婪比對

貪婪模式:盡可能最多比對

非貪婪模式,盡可能最少比對,一般在量詞(*、+)後面加個問号就是非貪婪模式。

<code># 貪婪比對</code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"&lt;div&gt;.*&lt;/div&gt;"</code><code>, </code><code>"&lt;div&gt;a&lt;/div&gt;&lt;div&gt;b&lt;/div&gt;&lt;div&gt;c&lt;/div&gt;"</code><code>)</code>

<code>[</code><code>'&lt;div&gt;a&lt;/div&gt;&lt;div&gt;b&lt;/div&gt;&lt;div&gt;c&lt;/div&gt;'</code><code>]</code>

<code># 非貪婪比對</code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"&lt;div&gt;.*?&lt;/div&gt;"</code><code>, </code><code>"&lt;div&gt;a&lt;/div&gt;&lt;div&gt;b&lt;/div&gt;&lt;div&gt;c&lt;/div&gt;"</code><code>)</code>

<code>[</code><code>'&lt;div&gt;a&lt;/div&gt;'</code><code>, </code><code>'&lt;div&gt;b&lt;/div&gt;'</code><code>, </code><code>'&lt;div&gt;c&lt;/div&gt;'</code><code>]</code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"a(\d+)"</code><code>, </code><code>"a123b"</code><code>)     </code>

<code>[</code><code>'123'</code><code>]</code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"a(\d+?)"</code><code>, </code><code>"a123b"</code><code>)</code>

<code>[</code><code>'1'</code><code>]</code>

<code># 如果右邊有限定,非貪婪失效</code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"a(\d+)b"</code><code>, </code><code>"a123b"</code><code>)   </code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"a(\d+?)b"</code><code>, </code><code>"a123b"</code><code>)  </code>

貪婪比對是盡可能的向右比對,直到字元串結束。

非貪婪比對是比對滿足後就結束。

16.3 了解擴充表達式

以一個字元串來學習斷言的用法:"A regular expression "

1)(?=...)

正先行斷言,比對後面能比對的表達式。

有兩個re字元串,隻想比對regular中的:

<code>&gt;&gt;&gt; re.findall(r</code><code>"..(?=gular)"</code><code>, </code><code>"A regular expression"</code><code>) </code>

<code>[</code><code>'re'</code><code>]</code>

<code># 再向後比對幾個字元說明比對的regular中的。下面都會說明下,不再注釋</code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"(?=gular).{5}"</code><code>, </code><code>"A regular expression"</code><code>)</code>

<code>[</code><code>'gular'</code><code>]</code>

2)(?!...)

負先行斷言,比對後面不能比對表達式。

隻想比對expression中的re字元串,排除掉regular單詞:

<code>&gt;&gt;&gt; re.findall(r</code><code>"re(?!g)"</code><code>, </code><code>"A regular expression"</code><code>) </code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"re(?!g).{5}"</code><code>, </code><code>"A regular expression"</code><code>)</code>

<code>[</code><code>'ression'</code><code>]</code>

3)(?&lt;=...)

正向後行斷言,比對前面能比對表達式。

隻想比對單詞裡的re,排除開頭的re:

<code>&gt;&gt;&gt; re.findall(r</code><code>"(?&lt;=\w)re"</code><code>, </code><code>"A regular expression"</code><code>)</code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"(?&lt;=\w)re."</code><code>, </code><code>"A regular expression"</code><code>)       </code>

<code>[</code><code>'res'</code><code>]</code>

在re前面有一個或多個字元,是以叫後行斷言,正則比對是從前向後,當遇到斷言時,會再向字元串前端檢測已掃描的字元,相對于掃描方向是向後的。

4)(?&lt;!...)

負向後行斷言,比對前面不能比對的表達式。

隻想比對開頭的re:

<code>&gt;&gt;&gt; re.findall(r</code><code>"(?&lt;!\w)re"</code><code>, </code><code>"A regular expression"</code><code>) </code>

<code>&gt;&gt;&gt; re.findall(r</code><code>"(?&lt;!\w)re."</code><code>, </code><code>"A regular expression"</code><code>)</code>

<code>[</code><code>'reg'</code><code>]</code>

16.4 修飾符

re.VERBOSE上面說明可能你還不太明白,怎麼個更加可讀呢,這就來看看,下面兩個正則編譯等效:

<code>&gt;&gt;&gt; a </code><code>=</code> <code>re.</code><code>compile</code><code>(r</code><code>"""\d +  # the integral part</code>

<code>...                    \.    # the decimal point</code>

<code>...                    \d *  # some fractional digits"""</code><code>, re.X)</code>

<code>&gt;&gt;&gt; b </code><code>=</code> <code>re.</code><code>compile</code><code>(r</code><code>"\d+\.\d*"</code><code>)</code>

當你寫的正則很長的時候,可以添加注釋。

本文轉自 李振良OK 51CTO部落格,原文連結:http://blog.51cto.com/lizhenliang/1877675,如需轉載請自行聯系原作者