Python中的正則表達式

簡介

  • 正則表達式 是包含文本和特殊字符的字符串, 爲高級的文本模式匹配, 抽取, 與文本形式的搜索和替換功能提供了基礎
  • Python經過標準庫re模塊來支持正則表達式
  • 模式匹配的兩種方法完成匹配(模式匹配)php

    • 搜索(search())
    • 匹配(match())

特殊符號和字符

元字符指正則表達式中的特殊符號和字符。html

符號

符號 描述 示例
literal 匹配文本字符串的字面值literal foo
`re1\ re2` 匹配正則表達式re1re2 `foo\ bar`
. 匹配任何字符(除\n以外) f.o
^ 匹配字符串起始部分 ^foo
$ 匹配字符串終止部分 bar$
* 匹配0次或者屢次前面出現的正則表達式 [A-Za-z0-9]*
+ 匹配1次或者屢次前面出現的正則表達式 [a-z]+\\.com
? 匹配0次或者1次前面出現的正則表達式 goo?
{N} 匹配N次前面出現的正則表達式 [0-9]{3}
{M,N} 匹配M~N次前面出現的正則表達式 [0-9]{5,9}
[…] 匹配來自字符集的任意單一字符 [aeiou]
[x-y] 匹配x~y範圍中的任意單一字符 [0-9], [A-Za-z]
[^…] 不匹配此字符集中出現的任何一個字符, 包括某一範圍的字符 [^aeiou], \[^A-Za-z]
`(* + ? {})?` 用於匹配上面頻繁出現/重複出現符號的非貪婪版本(*、+、?、{}) .*?[a-z]
(…) 匹配封閉的正則表達式,而後另存爲子組 `([0-9]{3})?,f(oo u)bar`

特殊符號

符號 描述 示例
\d 匹配任何十進制數字,與[0-9]一致 data\d+.txt
\D \d相反
\w 匹配任何字母數字字符,與[A-Za-z0-9]相同
\W \w相反
\s 匹配任何空格字符,與[\n\t\r\v\f]相同
\S \s相反
\N 匹配已保存的子組 N price:\1
\c 逐字匹配任何特殊字符c \.,\\,\*
\A 匹配字符串起始,與^相同
\Z 匹配字符串結束,與$相同

擴展符號

符號 描述 示例
(?iLmsux) 在正則表達式自己中嵌入一個或多個特殊特殊標記 (vs. via function/method) (?x),(?im)
(?:...) 匹配一個不用保存的分組 (?:\w+\.)
(?P<name>...) 使用名字表示的正則分組 (?P<data>)
(?#...) 表示註釋,全部內容會被忽略 (?#comment)
(?=...) 匹配條件是若是...出如今以後的位置,而不使用輸入字符串;稱做正向前視斷言(positive lookahead assertion) (?=.com)
(?!...) 匹配條件是若是...不出如今以後的位置,而不使用輸入字符串;稱做負向前視斷言(negative lookahead assertion) (?!.net)
(?<=...) 匹配條件是若是...出如今以前的位置,而不使用輸入字符串;稱做正向後視斷言(positive lookbehind assertion) (?<=800-)
(?<!...) 匹配條件是若是...不出如今以前的位置,而不使用輸入字符串;稱做負向後視斷言(negative lookbehind assertion) (?<!192\.168\.)
`(?(id/name)Y N)` Conditional match of regex Y if group with given id or name exists else N; \ N is optional `(?(1)y x)`

使用管道符匹配多個正則表達式

管道符號在正則表達式中又稱爲擇一匹配符,表示 從多個模式中選擇其一 的操做。python

正則表達式 匹配的字符串
`at home` at,home
`r2d2 c3po` r2d2,c3po
`bat bet bit` bat,bet,bit

匹配任意單個字符

句點(.)符號匹配除了換行符\n之外的任何字符。不管字母、數字、空格(不包括\n換行符)、可打印字符、不可打印字符,使用.均可以匹配。linux

正則表達式 匹配的字符串
f.0 匹配在字母fo之間的任意一個字符,如:fao,f9o,f#o
.. 任意兩個字符
.end 匹配在字符串end以前的任意一個字符

注意正則表達式

要顯式匹配一個句點符號自己,必須使用反斜線轉義句點符號的功能,例如\.shell

匹配起始或結尾

有些符號和相關的特殊字符用於在字符串的起始或結尾部分指定用於搜索的模式。編程

符號 位置
^\A 起始
$\Z 結尾

簡單示例以下。緩存

正則表達式 匹配的字符串
^Froms 任何以From做爲開頭的字符串
/bin/bash$ 任何以/bin/bash結尾的字符串
^Subject:hi$ 匹配Subject:hi

匹配單詞邊界

符號 說明
\b 匹配一個單詞的邊界
\B 匹配不是一個單詞的邊界

簡單示例以下。bash

正則表達式 匹配的字符串
the 任何包含the的字符串
\bthe 任何以the開始的字符串
\bthe\b 僅僅匹配單詞the
\Bthe 任何包含但並不以the做爲起始的字符串

建立字符集

使用[]建立字符集,能夠匹配某些特定字符。閉包

正則表達式 匹配的字符串
b[aeiu]t bat,bet,bit,but
[cr][23] c2,c3,r2,r3

限定範圍和否認

除了單字符外,字符集還支持匹配指定的字符範圍。兩個字符中間用連字符-鏈接,用於指定一個字符的範圍。若是^緊跟在左括號後面,這個符號就表示不匹配給定字符集中任何一個字符。

正則表達式 匹配的字符串
z.[0-9] z 後面跟着任何一個字符,而後跟着一個數字
[r-u][env-y][us] 等價於[rstu][envwxy][us] ,好比匹配res
[^aeiou] 匹配一個非元音字符
[^\t\n] 不匹配製表符或換行符

使用閉包操做符實現存在性和頻數匹配

符號 說明
* 匹配左側的正則表達式出現零次或屢次的情形,這稱做Kleene閉包
+ 匹配一次或屢次出現的正則表達式,這稱做正閉包操做符
? 操做符將匹配零次或者一次出現的正則表達式
{} 裏面或者是單值,或者是一對由逗號分隔的數值;{N}表示匹配N次;{N,M}表示匹配N~M

若是問號緊跟在任何使用閉包操做符的匹配後面,它將直接要求正則表達式引擎匹配儘量少的次數。當模式匹配使用分組操做符時,正則表達式引擎將試圖吸取匹配該模式的儘量多的字符,這一般叫作貪婪匹配。問號要求正則表達式引擎在當前正則表達式中儘量少地匹配字符。

簡單示例。

正則表達式 匹配的字符串
[dn]ot do,dot,no,not
0?[1-9] 可能存在前置0的數字
[0-9]{15,16} 匹配15或者16個數字
</?[^>]+> 匹配HTML標籤

表示字符集的特殊字符

有一些特殊字符可以表示字符集。

符號 描述
\d 匹配任何十進制數字,與[0-9]一致 data\d+.txt
\D \d相反
\w 匹配任何字母數字字符,與[A-Za-z0-9]相同
\W \w相反
\s 匹配任何空格字符,與[\n\t\r\v\f]相同
\S \s相反

簡單示例。

正則表達式 匹配的字符串
\w+-\d+ 一個由字母數字組成的字符串和一串由一個連字符分割的數字

使用圓括號指定分組

有時候不只想要知道整個字符串是否匹配咱們的標準,並且想要知道可否提取任何已經成功匹配的特定字符串或者子字符串,可使用分組來實現。

一對圓括號能夠實現一下任意一個功能:

  • 對正則表達式進行分組
  • 匹配子組

對正則表達式分組,能夠在整個正則表達式中使用重複的操做符。

使用圓括號進行分組的一個反作用就是,匹配模式的子字符串能夠保存起來供後續使用。這些子組可以被同一次的匹配或者搜索重複調用,或者提取出來用於後續處理。

匹配子組的重要性在於,不少時候除了進行匹配操做之外,咱們還想要提取所匹配的模式。

簡單示例以下。

正則表達式 匹配的字符串
\d+(\.\d*)? 表示簡單浮點數的字符串

擴展表示法

正則表達式的擴展表示法,以問號開始(?...)。他們一般用於在判斷匹配以前提供標記,實現一個前視或者後視匹配,或者條件檢查。

儘管這些符號使用圓括號,可是隻有(?P<name>) 表示一個分組匹配,其餘的都沒有建立一個分組。

正則表達式 匹配的字符串
(?:\w+\.)* 以句點做爲結尾的字符串,例如 google.,可是這些匹配不會保存下來供後續使用和數據檢索
(?#comment) 註釋
(?=.com) 若是一個字符串和後面跟着 .com 才作匹配操做,並不使用任何目標字符串
(?!.net) 若是一個字符串後面不是跟着.net才作匹配操做
(?<=800-) 若是字符串以前爲800-才作匹配
(?<!192\.168\.) 若是一個字符串以前不是192.168.才作匹配操做
`(?(1)y x)` 若是一個匹配組1(\1)存在,就與y匹配;不然與x匹配

Python中的正則表達式

在Python中,re模塊支持更強大並且更通用的Perl風格的正則表達式,該模塊容許多個線程共享同一個已編譯的正則表達式對象,也支持命名子組。

re模塊

re模塊函數

函數 描述
compile(pattern, flags=0) 使用任何可選的標記來編譯正則表達式的模式,而後返回一個正則表達式對象

re模塊函數和正則表達式對象的方法

函數 描述 返回值
match(pattern, string, flags=0) 使用帶有可選標記的正則表達式模式匹配字符串 匹配成功,返回匹配對象;若是失敗,返回None
search(pattern, string, flags=0) 使用可選標記搜索字符串中第一次出現的正則表達式模式 匹配成功,返回匹配對象;若是失敗,返回None
findall(pattern, string[, flags]) 查找字符串中全部(非重複)出現的正則表達式模式 匹配列表
finditer(pattern, string[, flags]) findall相同,但返回的不是列表 一個迭代器
split(pattern, string,max=0 ) 根據正則表達式的模式分隔符,split函數將字符串分割爲列表,而後返回成功匹配的列表,分割最多操做max次,默認分割全部匹配成功的位置 分割後的列表
sub(pattern, repl, string, count=0) 使用repl替換count次正則表達式的模式在字符串中出現的位置;默認替換全部 替換操做數目
purge() 清除隱式編譯的正則表達式模式;清除緩存

經常使用的匹配對象方法

函數 描述
group(num=0) 返回整個匹配對象;或者編號爲num的特定子組
groups(default=None) 返回一個包含全部匹配子組的元組(若是沒有成功匹配,則返回一個空元組)
groupdict(default=None) 返回一個包含全部匹配的命名子組的字典,全部的子組名稱做爲字典的鍵(若是沒有匹配成功返回一個空元組)

經常使用的模塊屬性

屬性 描述
re.I,re.IGNORECASE 不區分大小寫的匹配
re.L,re.LOCALE 根據所使用的本地語言環境經過\w,\W,\b,\B,\s,\S實現匹配
re.M,re.MULTILINE ^$分別匹配目標字符串中行的起始和結尾,而不是嚴格匹配整個字符串自己的起始和結尾
re.S,re.DOTALL . 一般匹配除了\n 以外的全部單個字符,該標記可使.匹配換行符
re.X, re.VERBOSE 經過反斜線轉義,不然全部空格加上#都被忽略

使用compile函數編譯正則表達式

  • 在Python中能夠經過兩種途徑使用正則表達式:

    • re模塊函數
    • 調用編譯後的正則表達式對象方法
  • 在Python中由兩類對象和正則表達式有關:

    • re.compile生成的表達式對象
    • 匹配對象(成功調用 match()search() 以後返回的對象)
  • 幾乎全部的re模塊函數均可以做爲regex對象的方法。
  • 能夠經過按位或操做符(|)合併使用多個標記
  • 可使用(?FLAG)將標記嵌入到正則表達式

    >>> re.match(r'(?i)the', 'the')
    <_sre.SRE_Match object; span=(0, 3), match='the'>
    >>> re.match(r'(?i)the', 'The')
    <_sre.SRE_Match object; span=(0, 3), match='The'>
    >>> re.match(r'the', 'The')
    >>>

匹配對象以及group()groups()方法

匹配對象是成功調用match()或者search()返回的對象。匹配對象有兩個主要的方法: group()groups()

group()要麼返回整個匹配對象,要麼根據要求返回特定子組。groups()則僅返回一個包含惟一或者所有子組的元組。若是沒有子組的要求,那麼當group()仍返回整個匹配時,groups()返回一個空元組。

Python正則表達式容許命名匹配。

使用match方法匹配字符串

match方法試圖從字符串的起始部分對模式進行匹配。

若是匹配成功,就返回一個匹配對象;若是匹配對象失敗,就返回None。

匹配對象的group()方法可以用於顯示那個成功的匹配。

Python 3.5.4rc1 (default, Jul 25 2017, 08:53:34) 
[GCC 6.4.0 20170704] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> m = re.match('foo', 'foo')
>>> if m is not None:
...     m.group()
... 
'foo'
>>> m
<_sre.SRE_Match object; span=(0, 3), match='foo'>

只要模式從字符串的起始部分匹配,即便字符串比模式長,匹配也仍然可以成功。匹配結果是從較長字符串中抽取的匹配部分

>>> re.match('foo', 'food on the table').group()
'foo'

使用search在字符串中查找模式

search函數在任意位置對給定正則表達式模式搜索第一次出現的匹配狀況。若是搜索到成功的匹配,就會返回一個匹配對象;不然,返回None。

match相比,match只能從起始開始匹配,search能夠匹配任意位置。

>>> m = re.match('foo', 'seafood')
>>> m.group() if m is not None else print(m)
None
>>> m = re.search('foo', 'seafood')
>>> m.group() if m is not None else print(m)
'foo'

上面代碼,使用match匹配失敗,使用search則匹配成功。

匹配多個字符串

管道符號或擇一匹配符號(|)的使用。

>>> bt = 'bat|bet|bit'
>>> m = re.match(bt, 'bat')
>>> m.group() if m is not None else print(m)
'bat'
>>> m = re.match(bt, 'blt')                   # 不能匹配
>>> m.group() if m is not None else print(m)
None
>>> m = re.match(bt, 'He bit me')          # 不能匹配
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(bt, 'He bit me')      # 能夠搜索到
>>> m.group() if m is not None else print(m)
'bit'

匹配任何單個字符

點號.不能匹配換行符和空字符串

>>> dote = '.end'
>>> m = re.match(dote, 'bend')  # OK
>>> m.group() if m is not None else print(m)
'bend'
>>> m = re.match(dote, 'end')   # 不能匹配空內容
>>> m.group() if m is not None else print(m)
None
>>> m = re.match(dote, '\nend') # 不能匹配換行符
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(dote, 'The end.')  # 搜索匹配
>>> m.group() if m is not None else print(m)
' end'

使用轉義符,匹配點號。

>>> pi = '3.14'
>>> pit = '3\.14'
>>> m = re.match(pit, '3.14')   # 精確匹配
>>> m.group() if m is not None else print(m)
'3.14'
>>> m = re.match(pi, '3014')    # 點號匹配0
>>> m.group() if m is not None else print(m)
'3014'
>>> m = re.match(pi, '3.14')    # 點號匹配.
>>> m.group() if m is not None else print(m)
'3.14'

建立字符集

>>> selc = '[cr][23][dp][o2]'
>>> m = re.match(selc, 'c3po')
>>> m.group() if m is not None else print(m)
'c3po'
>>> m = re.match(selc, 'c3p1')
>>> m.group() if m is not None else print(m)
None

重複、特殊字符以及分組

正則表達式中最多見的狀況包括:

  • 特殊字符的使用
  • 正則表達式模式的重複出現
  • 使用圓括號對匹配模式的各部分進行分組和提取操做

特殊字符與重複出現

匹配0~1箇中間子域名

>>> import re
>>> patt = '\w+@(\w+\.)?\w+\.com'
>>> m = re.match(patt, 'nobody@xxx.com')            # 匹配0箇中間子域名
>>> m.group() if m is not None else print(m)
'nobody@xxx.com'
>>> m = re.match(patt, 'nobody@xxx.yyy.com')        # 匹配1箇中間子域名
>>> m.group() if m is not None else print(m)
'nobody@xxx.yyy.com'
>>> m = re.match(patt, 'nobody@xxx.yyy.zzz.com')    # 不能匹配2箇中間子域名
>>> m.group() if m is not None else print(m)
None

匹配任意多個子域名

>>> patt = '\w+@(\w+\.)*\w+\.com'                    # 將 ? 替換爲 *
>>> m = re.match(patt, 'nobody@xxx.yyy.zzz.com')    # 匹配2箇中間子域名
>>> m.group() if m is not None else print(m)
'nobody@xxx.yyy.zzz.com'

分組

使用圓括號來匹配和保存子組,以便於後續處理。

使用group()groups()方法獲取分組,其二者區別:

  • group()

    • 訪問每一個獨立的子組
    • 獲取完整匹配(不傳遞參數)
  • groups()

    • 獲取一個包含全部匹配子組的元組
>>> m = re.match('(\w\w\w)-(\d\d\d)', 'abc-123')
>>> m.group()                   # 完整匹配
'abc-123'
>>> m.group(1)                  # 子組1
'abc'
>>> m.group(2)                  # 子組2
'123'
>>> m.groups()                  # 所有子組
('abc', '123')

一個完整示例

>>> m = re.match('ab', 'ab')            # 沒有分組
>>> m.group()                           # 完整匹配
'ab'
>>> m = re.match('ab', 'ab')            # 沒有分組
>>> m.group()                           # 完整匹配
'ab'
>>> m.groups()                          # 全部子組
()
>>>
>>> m = re.match('(ab)', 'ab')          # 一個子組
>>> m.group()                           # 完整匹配
'ab'
>>> m.group(1)                          # 子組1
'ab'
>>> m.groups()                          # 所有子組
('ab',)
>>>
>>> m = re.match('(a)(b)', 'ab')        # 兩個子組
>>> m.group()
'ab'
>>> m.group(1)                          # 子組1
'a'
>>> m.group(2)                          # 子組2
'b'
>>> m.groups()                          # 所有子組
('a', 'b')
>>>
>>> m = re.match('(a(b))', 'ab')        # 兩個嵌套子組
>>> m.group()                           # 完整匹配
'ab'
>>> m.group(1)                          # 子組1
'ab'
>>> m.group(2)                          # 子組2
'b'
>>> m.groups()                          # 所有子組
('ab', 'b')

匹配字符串的起始和結尾以及單詞邊界

>>> m = re.search('^The', 'The end.')   # 匹配
>>> m.group() if m is not None else print(m)
'The'
>>> m = re.search('^The', 'end. The')   # 不作爲開始
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(r'\bthe', 'bite the dog')     # 匹配左側邊界
>>> m.group() if m is not None else print(m)
'the'
>>> m = re.search(r'\bthe', 'bitethe dog')      # 匹配左側邊界
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(r'\Bthe', 'bitethe dog')      # 匹配左側沒有邊界
>>> m.group() if m is not None else print(m)
'the'
>>> m = re.search(r'\Bthe\B', 'bitethe dog')    # 匹配兩側沒有邊界
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(r'\Bthe\b', 'bitethe dog')    # 匹配左側沒有邊界,右側有邊界
>>> m.group() if m is not None else print(m)
'the'

使用findall和finditer查找每一次出現的位置

findall()

  • 查詢字符串中某個正則表達式模式所有的非重複出現狀況
  • match()search()的區別是,findall()老是返回一個列表

finditer()findall()相似,不過返回結果是一個迭代器。

>>> re.findall('car', 'car')
['car']
>>> re.findall('car', 'carry')
['car']
>>> re.findall('car', 'carry the barcardi to the car')
['car', 'car', 'car']

結合分組使用

>>> s = 'This and that.'
>>> re.findall(r'(th\w+) and (th\w+)', s, re.I)
[('This', 'that')]
>>> list(re.finditer(r'(th\w+) and (th\w+)', s, re.I))[0].groups()
('This', 'that')
>>> list(re.finditer(r'(th\w+) and (th\w+)', s, re.I))[0].group(1)
'This'
>>> list(re.finditer(r'(th\w+) and (th\w+)', s, re.I))[0].group(2)
'that'

單個分組的多重匹配

  • 若是模式中只有一個分組,則匹配結果做爲結果集合的單個元素
  • 若是模式中由多個分組,則匹配結果爲元組,做爲結果集的單個元素
>>> s = 'this and that'
>>> re.findall(r'(th\w+) and (th\w+)', s, re.I)
[('this', 'that')]
>>> re.findall(r'(th\w+)', s, re.I)
['this', 'that']
>>>
>>>
>>> s = 'This and that. What, where, when, and who'
>>> re.findall(r'th\w+|wh\w+', s, re.I)
['This', 'that', 'What', 'where', 'when', 'who']
>>> re.findall(r'(th\w+)|(wh\w+)', s, re.I)
[('This', ''), ('that', ''), ('', 'What'), ('', 'where'), ('', 'when'), ('', 'who')]
>>> re.findall(r'(wh\w+)', s, re.I)
['What', 'where', 'when', 'who']
>>>
>>>
>>> s = 'This where. That when. There who.'
>>> re.findall(r'(th\w+)\s(wh\w+)', s, re.I)
[('This', 'where'), ('That', 'when'), ('There', 'who')]

使用sub和subn搜索和替換

有兩個函數用於實現搜索和替換功能: sub()subn()sub()返回一個替換後的字符串;subn()還返回一個表示替換的總數,替換後的字符串和替換總數做爲元組返回。

>>> re.sub('X', 'Mr. Smith', 'attn: X\n\nDear X,\n')
'attn: Mr. Smith\n\nDear Mr. Smith,\n'
>>> re.subn('X', 'Mr. Smith', 'attn: X\n\nDear X,\n')
('attn: Mr. Smith\n\nDear Mr. Smith,\n', 2)
>>> re.sub('[ae]', 'X', 'abcdef')
'XbcdXf'
>>> re.subn('[ae]', 'X', 'abcdef')
('XbcdXf', 2)

使用匹配對象的group()方法除了可以取出匹配分組編號外,還可使用\N,其中N是在替換字符串中使用的分組編號。

>>> re.sub(r'(\d{1,2})/(\d{1,2})/(\d{2}|\d{4})', r'\2/\1/\3', '2/20/1992')
'20/2/1992'
>>> re.sub(r'(\d{1,2})/(\d{1,2})/(\d{2}|\d{4})', r'\2/\1/\3', '2/20/92')
'20/2/92'

在限定模式上使用split分隔字符串

split 基於正則表達式的模式分隔字符串。能夠經過爲max參數設定一個值(非零)來指定最大分割數。

>>> import re
>>> DATA = ()
>>> DATA = (
...     'Mountain View, CA 94040',
...     'Sunnyvale, CA',
...     'Los Altos, 94023',
...     'Cupertino 95014',
...     'Palo Alto CA'
... )
>>> for datum in DATA:
...     print(re.split(', |(?= (?:\d{5}|[A-Z]{2})) ', datum))
...
['Mountain View', 'CA', '94040']
['Sunnyvale', 'CA']
['Los Altos', '94023']
['Cupertino', '95014']
['Palo Alto', 'CA']

擴展符號

經過使用(?iLmsux)系列選項,用戶能夠直接在正則表達式裏面指定一個或者多個標記。

re.I/re.IGNORECASE, re.M/MULTILINE

>>> re.findall(r'(?i)yes', 'yes? Yes. YES!~')        # 忽略大小寫
['yes', 'Yes', 'YES']
>>> re.findall(r'(?i)th\w+', 'The quickest way is through this tunnel.')    # 忽略大小寫
['The', 'through', 'this']
>>> re.findall(r'(?im)(^th[\w ]+)', """                # 忽略大小寫;多行
... This line is the first,
... another line,
... that line, it's the best.
... """)
['This line is the first', 'that line']

re.S/re.DOTALL

使用re.S,re.DOTALL標記,使得點號.可以用來表示換行符。

>>> re.findall(r'th.+', '''
... The first line
... the second line
... the third line
... ''')
['the second line', 'the third line']
>>> re.findall(r'(?s)th.+', '''
... The first line
... the second line
... the third line
... ''')
['the second line\nthe third line\n']

re.X/re.VERBOSE

re.X/re.VERBOSE標記容許用戶經過抑制在正則表達式中使用空白符(除了在字符類中或者在反斜線轉義中)來建立更易讀的正則表達式。

>>> re.search(r'''(?x)
...     \((\d{3})\)     # 匹配區號
...     [ ]             # 匹配空格
...     (\d{3})         # 匹配前綴
...     -               # 橫線
...     (\d{4})         # 終點數字
... ''', '(800) 555-1212').groups()
('800', '555', '1212')

(?:...)

經過使用(?:...)符號,能夠對部分正則表達式進行分組,可是並不會保存該分組用於後續的檢索或應用。

>>> re.findall(r'http://(?:\w+\.)*(\w+\.com)',
...             'http://goole.com http://www.google.com http://code.google.com')
['goole.com', 'google.com', 'google.com']
>>> re.search(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?:\d{4})',
...             '(800) 555-1212').groupdict()
{'areacode': '800', 'prefix': '555'}

結合\g,使用(?P<name>)(?P=name)符號。

>>> re.sub(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?:\d{4})',
...     '(\g<areacode>) \g<prefix>-xxxx', '(800) 555-1212')
'(800) 555-xxxx'

檢索元組

  • 對於沒有命名的元組,使用\N,其中N爲數字
  • 對於命名的元組,可使用\g<name>,也可使用\N
>>> m = re.search(r'(\d{4}) (?P<prefix>\d{4}) (\d{3})', '0530 8023 123')
>>> m.group()
'0530 8023 123'
>>> m.group(1)
'0530'
>>> m.group(2)
'8023'
>>> m.group(3)
'123'
>>> m.groupdict()
{'prefix': '8023'}
>>>
>>> re.sub(r'(\d{4}) (\d{4}) (\d{3})', r'\3 \2 \1', '0530 8023 123')
'123 8023 0530'
>>> re.sub(r'(\d{4}) (?P<prefix>\d{4}) (\d{3})', r'\3 \2 \1 - \g<prefix>', '0530 8023 123')
'123 8023 0530 - 8023'

在一個相同的正則表達式中重用模式

>>> re.match(r'(?P<num>\d{2})(?P=num)', '1212')            # 只能匹配相同的內容
>>> print(m) if m is None else m.group()
'1212'
>>> m = re.match(r'(?P<num>\d{2})(?P=num)', '1234')        # 不能匹配不一樣的內容
>>> print(m) if m is None else m.group()
None
>>> bool(re.match(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?P<number>\d{4}) (?P=areacode)-(?P=prefix)-(?P=number)',
...     '(800) 555-1212 800-555-1212'))
True
>>> bool(re.match(r'''(?x)
...     # match (800) 555-1212, save areacode, prefix, no.
...     \((?P<areacode>\d{3})\)[ ](?P<prefix>\d{3})-(?P<number>\d{4})
... 
...     # space
...     [ ]
... 
...     # match 800-555-1212
...     (?P=areacode)-(?P=prefix)-(?P=number)
... 
...     # space
...     [ ]
... 
...     # match 18005551212
...     1(?P=areacode)(?P=prefix)(?P=number)
... ''', '(800) 555-1212 800-555-1212 18005551212'))
True

前視匹配

可使用(?=...)(?!...) 符號在目標字符串中實現一個前視匹配,而沒必要實際使用這些字符串。

  • (?=...) : 正向前視斷言
  • (?!...) : 負向前視斷言
>>> re.findall(r'\w+(?= van Rossum)',    # 正向前視斷言
... '''
...     Guido van Rossum
...     Tim Peters
...     Alex Martelli
...     Just van Rossum
...     Raymond Hettinger
... ''')
['Guido', 'Just']
>>> re.findall(r'(?m)^\s+(?!noreply|postmaster)(\w+)',    # 負向前視斷言
... '''
...     sales@phptr.com
...     postmaster@phptr.com
...     eng@phptr.com
...     noreply@phptr.com
...     admin@phptr.com
... ''')
['sales', 'eng', 'admin']
>>> ['%s@aw.com' % e.group(1) for e in \
... re.finditer(r'(?m)^\s+(?!noreply|postmaster)(\w+)',
... '''
...     sales@phptr.com
...     postmaster@phptr.com
...     eng@phptr.com
...     noreply@phptr.com
...     admin@phptr.com
... ''')]
['sales@aw.com', 'eng@aw.com', 'admin@aw.com']

條件正則表達式匹配

(?(id/name)yes-pattern|no-pattern)

Will try to match with yes-pattern if the group with given id or name exists, and with no-pattern if it doesn’t. no-pattern is optional and can be omitted. For example, (<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) is a poor email matching pattern, which will match with '<user@host.com>' as well as 'user@host.com', but not with '<user@host.com' nor 'user@host.com>'.
>>> r = r'(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)'
>>> m = re.match(r, '<user@host.com>')
>>> print(m) if m is None else m.group()
'<user@host.com>'
>>> m = re.match(r, 'user@host.com')
>>> print(m) if m is None else m.group()
'user@host.com'
>>> m = re.match(r, '<user@host.com')
>>> print(m) if m is None else m.group()
None
>>> m = re.match(r, 'user@host.com>')
>>> print(m) if m is None else m.group()

貪婪搜索

加入由一系列相似以下格式的字符串

Thu Feb 15 17:32:12 2007::szhkai@qq.com::1123242-3-5

咱們所感興趣的是,數據記錄內包含由連字符鏈接的三個整數的整行數據

>>> s = '''
Thu Feb 15 17:41:42 2007::szhkai@qq.com::1123242-3
Sun Jul 22 13:32:25 2007::szhkai@qq.com::1123242-5
The May 12 17:02:52 2007::szhkai@qq.com::1123242-3-5
Thu Apr 18 12:22:42 2007::szhkai@qq.com::12323-3-5
'''
>>> re.findall(r'(?m).+\d+-\d+-\d+', s)
['\tThe May 12 17:02:52 2007::szhkai@qq.com::1123242-3-5', '\tThu Apr 18 12:22:42 2007::szhkai@qq.com::12323-3-5']

若是咱們對\d+-\d+-\d+這一部分感興趣,可使用元組提取

>>> re.findall(r'(?m).+(\d+-\d+-\d+)', s)
['2-3-5', '3-3-5']

可是不能提取第一個整數。這是由於正則表達式在實現上是採用貪婪匹配,試圖匹配該模式儘量多的字符。可使用非貪婪操做符?解決這個問題。能夠在*, +, ?後使用?。該操做符要求正則表達式引擎匹配儘量少的字符。在.+後放置一個?能夠得到指望的結果。

>>> re.findall(r'(?m).+?(\d+-\d+-\d+)', s)
['1123242-3-5', '12323-3-5']

注意事項

ASCII碼衝突

若是符號同時使用於ASCII碼和正則表達式特殊符號,就會出現問題,如\b表示ASCII字符的退格符,可是\b同時也是一個正則表達式的特殊符號,表示匹配一個單詞的邊界。對於正則表達式編譯器而言,若將\b視爲正則表達式特殊字符,須要使用\進行轉義。

>>> m = re.match('\bblow', 'blow')      # 退格鍵; 沒有匹配
>>> print(m) if m is None else m.group()
None
>>> m = re.match('\\bblow', 'blow')     # 匹配單詞邊界
>>> print(m) if m is None else m.group()
'blow'
>>> m = re.match(r'\bblow', 'blow')     # 使用 raw string
>>> print(m) if m is None else m.group()
'blow'

w和W字母數字字符集同時受re.L/LOCALEUnicode(re.U/UNICODE)標記影響。

參考

說明

Python版本

# 對於Python2
bovenson@ThinkCentre:~$ python2
Python 2.7.13+ (default, Jul 19 2017, 18:15:03) 
[GCC 6.4.0 20170704] on linux2
Type "help", "copyright", "credits" or "license" for more information.

# 對於Python3
bovenson@ThinkCentre:~$ python3
Python 3.5.4rc1 (default, Jul 25 2017, 08:53:34) 
[GCC 6.4.0 20170704] on linux
Type "help", "copyright", "credits" or "license" for more information.
相關文章
相關標籤/搜索