Regular Expression:Python碰見正則表達式

正則表達式的基本概念

  正則表達式爲高級的文本模式匹配、抽取或文本形式的搜索和替換功能提供了基礎。
  簡單地說,正則表達式(Regular Expression,簡稱爲 regex)是一些由字符和特殊符號組成的字符串,它們描述了模式的重複或者表述多個字符,因而正則表達式能按照某種模式匹配一系列有類似特徵的字符串。換句話說, 它們可以匹配多個字符串……
  術語「匹配」(matching),指的是術語「模式匹配」(pattern-matching)。在Python語言的術語中,主要有兩種方法完成模式匹配:「搜索」(searching)和「匹配」(matching)。搜索即在字符串任意部分中搜索匹配的模式;而匹配是指判斷一個字符串可否從起始處所有或者部分地匹配某個模式。搜索經過search()函數或方法來實現,而匹配經過調用match()函數或方法實現。總之,當涉及模式時,所有使用術語「匹配」。php

第一部分供快速查閱,正則表達式在Python中的用法在第二部分介紹。python

老司機專屬通道

  1. 元字符^$*+?.|\(){}[]
  • 正則表達式由兩種基本字符類型組成:原義文本字符和元字符
  • 元字符是正則表達式中有特殊含義的非字母字符,這裏的特殊含義要根據上下文語義來判斷,含義並不惟一。
  1. 量詞?+*{n}{n,m}{n,}
  2. 字符類
  • 使用元字符[]來構建一個簡單的類。
  • 字符類取反——^
  • 範圍類,如[0-9a-b]
  1. 預約義類及其等價類
字符 等價類
. [^\r\n]
\d [0-9]
\D [^0-9]
\s [\t\n\r\f\v]
\S [^\t\n\r\f\v]
\w [a-zA-Z0-9_]
\W [^a-zA-Z0-9_]
  1. 邊界^$\b\B
  • ^$分別表明串開始和串結束
  • \b\B分別表明單詞邊界和費單詞邊界
  1. 分組與或()|


爲何要使用正則表達式

先來看幾個使用字符串方法的例子:web

# 字符串匹配就能實現
# 場景1. 找到course開頭的語句
def find_start_str(file_name, start_str):
    file = open(file_name)

    for line in file:
        if line.startswith(start_str):
            print(line)

    file.close()

find_start_str('course.txt', 'course')


# 場景2, 找到course開頭和2019結尾的語句
def find_start_end_str(file_name, start_str, end_str):
    file = open(file_name)

    for line in file:
        if line.startswith(start_str)\
                and line[:-1].endswith(end_str):  # Python是以\n結束的
            print(line)

    file.close()

find_start_end_str('course.txt', 'course', '2019')


# 場景3,匹配一個下劃線或者字母開頭的變量名
a = "_valuel"
print(a and(a[0] == '_' or 'a' <= a[0] <= 'Z'))

course.txt文件:正則表達式

course Java 2019
course Html 2018
course Python 2010
course C++ 2018
course Python3 2019

C 0000
C# 0000
.net 0000
php 0000

問題:每一次匹配都要單獨寫函數完成,有沒有相應簡單的方法?express

Python正則表達式re模塊

  1. re.Match

Match對象是一次匹配的結果,包含了不少關於這次匹配的信息,可使用Match提供的可讀屬性或方法來獲取這些信息。python3.x

屬性:app

  • endpos:The index into the string beyond which the RE engine will not go.文本中正則表達式結束搜索的索引。值與Pattern.match()和Pattern.seach()方法的同名參數相同。
  • lastgroup:The name of the last matched capturing group.最後一個被捕獲的分組的別名。若是這個分組沒有別名或者沒有被捕獲的分組,將爲None。
  • lastindex:The integer index of the last matched capturing group.最後一個被捕獲的分組在文本中的索引。若是沒有被捕獲的分組,將爲None。
  • pos:The index into the string at which the RE engine started looking for a match.文本中正則表達式開始搜索的索引。值與Pattern.match()和Pattern.seach()方法的同名參數相同。
  • re:The regular expression object.匹配時使用的Pattern對象。
  • regs
  • string:The string passed to match() or search().匹配時使用的文本。

方法:ide

  • end(self, group=0, /)
    Return index of the end of the substring matched by group.
    返回指定的組截獲的子串在string中的結束索引(子串最後一個字符的索引+1)。group默認值爲0。
  • expand(self, /, template)
    Return the string obtained by doing backslash substitution on the string template, as done by the sub() method.
    將匹配到的分組代入template中而後返回。template中可使用\id或\g、\g引用分組,但不能使用編號0。\id與\g是等價的;但\10將被認爲是第10個分組,若是你想表達\1以後是字符’0’,只能使用\g<1>0。
  • group(...)
    group([group1, …]) -> str or tuple.
    Return subgroup(s) of the match by indices or names.
    For 0 returns the entire match.
    得到一個或多個分組截獲的字符串;指定多個參數時將以元組形式返回。group1可使用編號也可使用別名;編號0表明整個匹配的子串;不填寫參數時,返回group(0);沒有截獲字符串的組返回None;截獲了屢次的組返回最後一次截獲的子串。
  • groupdict(self, /, default=None)
    Return a dictionary containing all the named subgroups of the match, keyed by the subgroup name.
    default
    Is used for groups that did not participate in the match.
    返回以有別名的組的別名爲鍵、以該組截獲的子串爲值的字典,沒有別名的組不包含在內。default含義同上。
  • groups(self, /, default=None)
    Return a tuple containing all the subgroups of the match, from 1.
    default
    Is used for groups that did not participate in the match.
    以元組形式返回所有分組截獲的字符串。至關於調用group(1,2,…last)。default表示沒有截獲字符串的組以這個值替代,默認爲None。
  • span(self, group=0, /)
    For match object m, return the 2-tuple (m.start(group), m.end(group)).
    返回(start(group), end(group))。
  • start(self, group=0, /)
    Return index of the start of the substring matched by group.
    返回指定的組截獲的子串在string中的起始索引(子串第一個字符的索引)。group默認值爲0。

更多請查閱:help(re.Match)svg

  1. re.Pattern對象

Pattern對象是一個編譯好的正則表達式,經過Pattern提供的一系列方法能夠對文本進行匹配查找。
Pattern不能直接實例化,必須使用re.compile()進行構造。函數

Pattern提供了幾個可讀屬性用於獲取表達式的相關信息:

  • flags:The regex matching flags.編譯時用的匹配模式。數字形式。
  • groupindex:A dictionary mapping group names to group numbers. 以表達式中有別名的組的別名爲鍵、以該組對應的編號爲值的字典,沒有別名的組不包含在內。
  • groups:The number of capturing groups in the pattern.表達式中分組的數量。
  • pattern:The pattern string from which the RE object was compiled.編譯時用的表達式字符串。

更多請查閱:help(re.Pattern)


兩種方式:
>ipython
In [1]: import re
In [3]: type(re.compile(r'course'))
Out[3]: re.Pattern
In [4]:
In [4]: re.match(r'course','course Python3.x').group()
Out[4]: 'course'
>ipython
In [1]: str = 'course python 2019'
In [2]: str.find('one')
Out[2]: -1
In [3]: str.find('py')
Out[3]: 7
In [4]: str.startswith('python')
Out[4]: False
In [5]: str.startswith('course')
Out[5]: True

In [6]: import re
In [7]: pa = re.compile(r'2019\n')
In [8]: type(pa)
Out[8]: re.Pattern
In [9]: re.Pattern.
                    findall()   fullmatch() match()     scanner()   sub()
                    finditer()  groupindex  mro()       search()    subn()
                    flags       groups      pattern     split()

In [9]: help(re.Pattern.match)
Help on method_descriptor:

match(self, /, string, pos=0, endpos=9223372036854775807)
    Matches zero or more characters at the beginning of the string.

In [10]:

注:r表明原始字符串,最大的做用是避免轉義


Match對象的屬性

  1. string 屬性:
    獲取匹配時使用的字符串對象獲取匹配時使用的字符串對象
>>> m = re.match(r'\d+','456abc')
>>> m.string
'456abc'
  1. re 屬性:
    匹配時使用的pattern對象,也就是匹配到內容的正則表達式對象匹配時使用的pattern對象,也就是匹配到內容的正則表達式對象
>>> m
<_sre.SRE_Match object at 0x02C8FA68>

>>> m.re
<_sre.SRE_Pattern object at 0x02D4ECD0>
  1. pos屬性:
    該屬性表示文本中正則表達式開始搜索的索引。值與Pattern.match()和Pattern.seach()方法中的同名參數相同
>>> m.pos
0
  1. endpos屬性:
    該屬性表示文本中正則表達式結束搜索的索引。值與Pattern.match()和 Pattern.seach()方法中的同名參數相同
>>> m.endpos
6
  1. lastindex屬性:
    該屬性表示最後一個被捕獲的分組在文本中的索引。若是沒有被捕獲的分組,將爲None
>>> m= re.match(r'a(b)(c)d','abcdef')
>>> m.lastindex
2
  1. lastgroup屬性:
    該屬性表示最後一個被捕獲的分組別名。若是這個分組沒有別名或者沒有被捕獲的分組,將爲None。

  2. group([group1, …]):
    得到一個或多個分組截獲的字符串;指定多個參數時將以元組形式返回。group1可使用編 號也可使用別名;編號0表明匹配的整個子串;默認返回group(0)
    實例:group函數傳多個參數

p = re.compile('(a(b)c)d')
m = p.match('abcd')
resTup = m.group(1,2,1)
print resTup
>>>('abc', 'b', 'abc')
  1. groups([default=None])
    以元組形式返回所有分組截獲的字符串。至關於調用group(1,2,…last)

  2. start([group=0])
    返回指定的組截獲的子串在string中的起始索引(子串第一個字符的索引)。默認爲第0組,即整個字符串

  3. end([group=0])
    返回指定的組截獲的子串在string中的結束索引(子串最後一個字符的索引)。group默認值 爲0,即整個字符串

  4. span([group])
    該方法表示以元組的形式返回 (start(group), end(group)),即某個分組的匹配文字內容在被 匹配字符串的開始索引位置和結束索引位置

  5. expand(template)
    將匹配到的分組代入template中而後返回。template中可使用\id或\g、\g 引用分組,但不能使用編號0。\id與\g是等價的;但\10將被認爲是第10個分組,若是 你想表達\1以後是字符’0’,只能使用\g<1>0。

m = re.search(r'(\w+)! (\w+) (\w+)','HMan! How finny!')
# 將匹配的結果帶入 print m.expand(r'resut:\3 \2 \1') 
>>> resut:finny How HMan
  1. groupdict([default=None])
    該函數的做用是,將全部匹配到而且指定了別名的分組,以別名爲key,匹配到的字串爲value, 存於字典中,而後返回這個字典。若是表達式中未設置別名分組,就會返回一個空字典
>>> m = re.search(r'(?P<num>\d+)(\w+)','78fd')
>>> m.groupdict()
{'num': '78'}

match()方法

  • 匹配字符串開頭的零個或多個字符。返回一個match對象或None。
  • 可選參數pos和endpos分別指定被匹配字符串的開始和結束位置
In [10]: str
Out[10]: 'course python 2019'

In [11]: pa.match(str)
Out[11]: <re.Match object; span=(0, 6), match='course'>

In [12]: ma = pa.match(str)

In [13]: ma
Out[13]: <re.Match object; span=(0, 6), match='course'>

In [14]: type(ma)
In [15]: re.Match.
                   end()       group()     lastgroup   pos         span()
                   endpos      groupdict() lastindex   re          start()
                   expand()    groups()    mro()       regs        string

In [15]: ma.group()
Out[15]: 'course'

In [16]: help(re.Match.group)
Help on method_descriptor:

group(...)
    group([group1, ...]) -> str or tuple.
    Return subgroup(s) of the match by indices or names.
    For 0 returns the entire match.

In [17]:

group()方法

  • 返回一個字符串或者元組
  • 有括號括起來返回元組,不然返回字符串

匹配結果在原始字符串的索引位置:span()方法

In [17]: ma.span()
Out[17]: (0, 6)

In [18]:

被匹配字符串:string屬性

In [18]: ma.string
Out[18]: 'course python 2019'

Pattern實例:re屬性

In [19]: ma.re
Out[19]: re.compile(r'course', re.UNICODE)

In [20]:

忽略大小寫:

In [20]: pa
Out[20]: re.compile(r'course', re.UNICODE)

In [21]: pa = re.compile('course', re.I)

In [22]: pa
Out[22]: re.compile(r'course', re.IGNORECASE|re.UNICODE)

IIn [23]: ma = pa.match('Course: Python3.x \n course:etc...')

In [24]: ma.group()
Out[24]: 'Course'

In [25]: ma = pa.match('Couse: Python3.x \n course:etc...')

In [26]: ma.group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-ad89060ab833> in <module>
----> 1 ma.group()

AttributeError: 'NoneType' object has no attribute 'group'

In [27]: pa = re.compile('course')

In [28]: pa
Out[28]: re.compile(r'course', re.UNICODE)

In [29]: ma = pa.match('Couse: Python3.x \n course:etc...')

In [30]: ma.group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-18-ad89060ab833> in <module>
----> 1 ma.group()

AttributeError: 'NoneType' object has no attribute 'group'

In [31]:

(元)組的形式返回groups()

In [31]: pa = re.compile(r'(course)', re.I)

In [32]: pa
Out[32]: re.compile(r'(course)', re.IGNORECASE|re.UNICODE)

In [33]: pa.match('Course: Python3.x \n course:etc...').group()
Out[33]: 'Course'

In [34]: pa.match('Course: Python3.x \n course:etc...').groups()
Out[34]: ('Course',)

In [35]: pa = re.compile(r'course', re.I)

In [36]: pa
Out[36]: re.compile(r'course', re.IGNORECASE|re.UNICODE)

In [37]: pa.match('Course: Python3.x \n course:etc...').group()
Out[37]: 'Course'

In [38]: pa.match('Course: Python3.x \n course:etc...').groups()
Out[38]: ()

In [39]: pa = re.compile(r'course', re.I)

In [40]: pa
Out[40]: re.compile(r'course', re.IGNORECASE|re.UNICODE)

In [41]: pa.match('Course: Python3.x \n course:etc...').group()
Out[41]: 'Course'

In [42]: pa.match('Course: Python3.x \n course:etc...').groups()
Out[42]: ()

In [43]: pa = re.compile(r'(course)', re.I)

In [44]: pa
Out[44]: re.compile(r'(course)', re.IGNORECASE|re.UNICODE)

In [45]: pa.match('Course: Python3.x \n course:etc...').group()
Out[45]: 'Course'

In [46]: pa.match('Course: Python3.x \n course:etc...').groups()
Out[46]: ('Course',)

In [47]:

關於groups()

In [52]: ma = pa.match('Course: Python3.x \n course:etc...')

In [53]: ma
Out[53]: <re.Match object; span=(0, 6), match='Course'>

In [54]: type(ma)
Out[54]: re.Match

In [55]: help(re.Match.groups)
Help on method_descriptor:

groups(self, /, default=None)
    Return a tuple containing all the subgroups of the match, from 1.

    default
      Is used for groups that did not participate in the match.

In [56]:
  1. 上面方法是先生成判斷對象再匹配字符串。下面介紹直接使用match方法:

  2. 直接使用match方法

In [56]: help(re.match)
Help on function match in module re:

match(pattern, string, flags=0)
    Try to apply the pattern at the start of the string, returning
    a Match object, or None if no match was found.


In [57]:

match()方法

  • 嘗試應用字符串開頭的模式,返回匹配對象,若是沒有找到匹配,則返回None。
In [57]: ma = re.match(r'course','course python3.x etc..')

In [58]: ma
Out[58]: <re.Match object; span=(0, 6), match='course'>

In [59]: type(ma)
Out[59]: re.Match

In [60]: ma.group()
Out[60]: 'course'

In [61]:

注:這種方式適合匹配次數較少的狀況,由於每次匹配都會生成一個Pattern對象。

In [10]: re.
             A              copyreg     enum            findall     functools
             L              Match       Pattern         S           split
             sub            TEMPLATE    UNICODE         ASCII       DEBUG
             error          finditer    I               LOCALE      match
             purge          Scanner     sre_compile     subn        template
             VERBOSE        compile     DOTALL          escape      fullmatch
             IGNORECASE     M           MULTILINE       RegexFlag   search
             sre_parse      T           U               X


In [10]: str
Out[10]: 'course python 2019'

In [11]: pa = re.compile(r'2019')

In [12]: pa.match(str)

In [13]: pa.search(str)
Out[13]: <re.Match object; span=(14, 18), match='2019'>

In [14]: sc = pa.search(str)

In [15]: sc.group
Out[15]: <function Match.group>

In [16]: sc.group()
Out[16]: '2019'

In [17]: pa = re.compile(r'course')

In [18]: pa.match(str).group()
Out[18]: 'course'

In [22]: help(sc.group)
Help on built-in function group:

group(...) method of re.Match instance
    group([group1, ...]) -> str or tuple.
    Return subgroup(s) of the match by indices or names.
    For 0 returns the entire match.


In [23]:

正則表達式語法

正則表達式語法1:

字符 匹配
. 匹配任意(除\n)字符
[] 匹配[]中包含字符串中任一字符
\d\D 匹配數字,非數字
\s\S 匹配空白(\t\n\r\f\v),非空白字符
\w\W 匹配單詞字符[a-zA-Z0-9_],非單詞字符

注:

  1. [...]匹配的是[]中包含字符的任意一個字符,如[ijk]匹配的是i、j、k任一字符;匹配多個字符的狀況詳下面。
  2. [\u4E00-\u9FA5]匹配任一漢字,英文空格不是漢字。
  3. [.]匹配的是.,再如[.*]匹配的是.*
  4. [\d]匹配的是任意數字,等價於[0-9]
  5. [^...]中的^表示字符類取反
  6. \t\n\r\f\v分別對應:水平製表符、換行符、回車符、換頁符、垂直製表符
In [3]: import re

In [4]: ma = re.match(r'{[abc]}','{b}')

In [5]: ma.group()
Out[5]: '{b}'

In [6]: ma = re.match(r'{[abc]}','{d}')

In [7]: type(ma)
Out[7]: NoneType

In [8]: ma = re.match(r'{[.]}','{d}')

In [9]: ma.group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-9-ad89060ab833> in <module>
----> 1 ma.group()

AttributeError: 'NoneType' object has no attribute 'group'

In [10]: ma = re.match(r'{.}','{d}')

In [11]: ma.group()
Out[11]: '{d}'

In [16]: ma = re.match(r'{\[[abcd]\]}','{[d]}')

In [17]: ma.group()
Out[17]: '{[d]}'

In [18]:

注:若是要匹配的模式包含[]須要轉義.

正則表達式語法2:

字符 匹配
* 匹配前一個元素0次或者無限次
+ 匹配前一個元素1次或者無限次
? 匹配前一個元素0次或者1次,非貪婪
{m}/{m,n}/{m,} 匹配前一個元素剛好m次或者至少m次但很少於n次或者至少m次
*?/+?/?? 匹配模式變爲非貪婪(儘量少匹配字符)
In [18]: ma = re.match(r'[A-Z][a-z]*','A')

In [19]: ma.group()
Out[19]: 'A'

In [20]: ma = re.match(r'[A-Z][a-z]','A')

In [21]: ma.group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-21-ad89060ab833> in <module>
----> 1 ma.group()

AttributeError: 'NoneType' object has no attribute 'group'

In [22]: ma = re.match(r'[A-Z][a-z]*','AbcdefgHIJK')

In [23]: ma.group()
Out[23]: 'Abcdefg'

In [24]:

一個例子:

Python變量命名規則

  • 變量名只能包含字母、數字和下劃線。變量名能夠字母或下劃線開頭,但不能以數字開頭。
  • 變量名不能包含空格,但可以使用下劃線來分隔其中的單詞。
  • 不要將Python關鍵字和函數名用做變量名,即不要使用Python保留用於特殊用途的單詞,如print。
  • 變量名應既簡短又具備描述性。
  • 慎用小寫字母l和大寫字母O,因給他們可能被人錯當作數字1和0;
    注意:應使用小寫的Python變量名。在變量名中使用大寫字母雖然不會致使錯誤,但避免使用大寫字母是個不錯的注意。

咱們重點關注前兩點,用正則表達式來描述:

In [38]: while(True):
    ...:     pa = re.compile(r'[_a-zA-Z][_\w]*')
    ...:     str = input("輸入字符串:")
    ...:     ma = pa.match(str)
    ...:     if ma != None:
    ...:         print(ma.group())
    ...:

In [44]: while(True):
    ...:     pa = re.compile(r'[_a-zA-Z][_a-zA-Z0-9]*')
    ...:     str = input("輸入:")
    ...:     ma = pa.match(str)
    ...:     if ma != None:
    ...:         print(ma.group())
    ...:

44是對38的改進,能夠嘗試38輸入__哈哈仍然匹配成功,而44中匹配獲得的是__

匹配0-99之間的數字:

In [5]: ma = re.match(r'[1-9]?[0-9]', '0')

In [6]: ma.group()
Out[6]: '0'

In [7]: ma = re.match(r'[1-9]?[0-9]', '10')

In [8]: ma.group()
Out[8]: '10'

In [9]: ma = re.match(r'[1-9]?[0-9]', '99')

In [10]: ma.group()
Out[10]: '99'

In [11]: ma = re.match(r'[1-9]?[0-9]', '100')

In [12]: ma.group()
Out[12]: '10'

In [13]:

匹配指定次數:

In [21]: re.match(r'[a-z]{2}','haha').group()
Out[21]: 'ha'

In [22]: re.match(r'[a-z]{2,3}','haha').group()
Out[22]: 'hah'

In [23]: re.match(r'[a-z]{2,4}','hahaxxxioflsaj').group()
Out[23]: 'haha'

In [24]: re.match(r'[a-z]{2,4}','ha3ha').group()
Out[24]: 'ha'

In [25]: re.match(r'[a-z]{2,4}','hah3ha').group()
Out[25]: 'hah'

In [26]: re.match(r'[a-z]{2,4}','hahx3ha').group()
Out[26]: 'hahx'

In [27]:

非貪婪模式:*?+???

In [28]: ma = re.match(r'[0-9][a-z]*','1abcdef').group()

In [29]: re.match(r'[0-9][a-z]*','1abcdef').group()
Out[29]: '1abcdef'

In [30]: re.match(r'[0-9][a-z]*?','1abcdef').group()
Out[30]: '1'

In [31]: re.match(r'[0-9][a-z]+?','1abcdef').group()
Out[31]: '1a'

In [32]: re.match(r'[0-9][a-z]?','1abcdef').group()
Out[32]: '1a'

In [33]: re.match(r'[0-9][a-z]+','1abcdef').group()
Out[33]: '1abcdef'

In [34]: re.match(r'[0-9][a-z]??','1abcdef').group()
Out[34]: '1'

In [35]: re.match(r'[0-9][a-z]?','1abcdef').group()
Out[35]: '1a'

In [36]:
非貪婪模式

貪婪匹配的限定符:*+?{n}{n,}{n,m}
非貪婪是在貪婪限定符後面多加一個?,以下表所示:

限定符 描述 模式 匹配
*? 匹配上一個元素零次或屢次,但次數儘量少 \d*?\.\d 「.0」,「19.9"和"219.9」
+? 匹配上一個元素一次或屢次,但次數儘量少 be+? 「been中的"be」,bent"中的"be"
?? 匹配上一個元素零次或一次,但次數儘量少 rai??n 「ran"和"rain」
{n}? 匹配前導元素剛好n次 ,\d{3}? "1.043.6"中的.043
{n,}? 匹配上一個元素至少n次,但次數儘量少 \d{2,}? 「166」,「29"和"1930」
{n,m}? 匹配上一個元素的次數介於n和m之間,但次數儘量少 \d{3,5}? 「166」,「16546」,「132654"中的"132」,「654」

測試代碼:

import re

line = ""
regex_re = ""
match_obj = re.match(regex_re, line)
if match_obj:
    print("# line = \"%s\"" % line)
    print("# regex_re = \"%s\"" % (regex_re,))
    print("# 貪婪以後:match_obj.group(1)->%s" % match_obj.group(1))
    # print("# match_obj.group()->%s" % match_obj.group())

驗證:

line = "boooooooobaaaaaoooooobbbbbaaabby123"
regex_re = ".*(b.*b).*"
# 貪婪:match_obj.group(1)->bb
match_obj.group()->boooooooobaaaaaoooooobbbbbbby123


line = "boooooooobaaaaaoooooobbbbbbby123"
regex_re = ".*(b.+b).*"
# 貪婪:match_obj.group(1)->bbb
match_obj.group()->boooooooobaaaaaoooooobbbbbbby123


line = "booooboooooobbbbbbaaaby123"
regex_re = ".*(b.*b).*"
# 貪婪:match_obj.group(1)->baaab
match_obj.group()->booooboooooobbbbbbaaaby123

line = "booooboooooobbbbbbaaaby123"
regex_re = ".*(b.+b).*"
# 貪婪:match_obj.group(1)->baaab
match_obj.group()->booooboooooobbbbbbaaaby123

line = "boooobaaaooobbbaaaaaaby123"
regex_re = ".*(b.{2,5}b).*"
# 貪婪:match_obj.group(1)->boooob
match_obj.group()->boooobaaaooobbbaaaaaaby123

line = "o你f"
regex_re = "(^o[\w]?f$)"
# 貪婪:match_obj.group(1)->o你f

# 注:group提取的是最外邊那個括號
line = "boobby123"
regex_re = "((bobby|boobby)123)"
# 貪婪:match_obj.group(1)->boobby123


line = "study in 四川大學"
regex_re = ".*([一-龥]+大學)"
# 貪婪:match_obj.group(1)->川大學

line = "study in 四川大學"
regex_re = ".*?([一-龥]+大學)"
# 非貪婪:match_obj.group(1)->四川大學


line = "of 出生於 1996年"
regex_re = ".*?([\d]+)"
# 非貪婪:match_obj.group(1)->1996
line = "of 出生於 1996年"
regex_re = ".*?(\d+)"
# 非貪婪:match_obj.group(1)->1996

line = "booooooooobby123"
regex_re = ".*(b.*b).*"
# 貪婪:match_obj.group(1)->bb

line = "booooooooobby123"
regex_re = ".*?(b.*b).*"
# 前非貪婪後貪婪:match_obj.group(1)->booooooooobb

line = "booooooooobby123"
regex_re = ".*?(b.*?b).*"
# 非貪婪:match_obj.group(1)->booooooooob

line = "booooooooobby123"
regex_re = ".*(b.*?b).*"
# 前貪婪後非貪婪:match_obj.group(1)->bb

line = "booooooooobijkby123"
regex_re = ".*(b.*?b).*"
# 前貪婪後非貪婪:match_obj.group(1)->bijkb

正則表達式語法3:邊界語法

字符 匹配
^ 匹配字符串開頭
$ 匹配字符串結尾
\A/\Z 指定字符串必須出如今開頭/結尾

問題引入:

In [37]: re.match(r'[a-zA-Z]{4,10}@onefine.top','123coding@onefine.top123').group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-37-5c2cf60a1190> in <module>
----> 1 re.match(r'[a-zA-Z]{4,10}@onefine.top','123coding@onefine.top123').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [38]: re.search(r'[a-zA-Z]{4,10}@onefine.top','123coding@onefine.top123').group()
Out[38]: 'coding@onefine.top'

In [39]: re.search(r'^[a-zA-Z]{4,10}@onefine.top$','123coding@onefine.top123').group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-39-70e4e4e6988c> in <module>
----> 1 re.search(r'^[a-zA-Z]{4,10}@onefine.top$','123coding@onefine.top123').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [40]: re.search(r'^[a-zA-Z]{4,10}@onefine.top','123coding@onefine.top123').group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-40-3c138c9aac5e> in <module>
----> 1 re.search(r'^[a-zA-Z]{4,10}@onefine.top','123coding@onefine.top123').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [41]: re.search(r'^[a-zA-Z]{4,10}@onefine.top$','123coding@onefine.top').group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-41-4ca35adfb0df> in <module>
----> 1 re.search(r'^[a-zA-Z]{4,10}@onefine.top$','123coding@onefine.top').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [42]: re.search(r'^[a-zA-Z]{4,10}@onefine.top$','coding@onefine.top').group()
Out[42]: 'coding@onefine.top'

In [43]: re.match(r'^[a-zA-Z]{4,10}@onefine.top$','coding@onefine.top').group()
Out[43]: 'coding@onefine.top'

In [45]: re.match(r'^[a-zA-Z]{4,10}@onefine.top','coding@onefine.top123').group()
Out[45]: 'coding@onefine.top'

In [46]:

\A/\Z 指定字符串必須出如今開頭/結尾,用法
指定必須以coding開頭,top結尾

In [52]: re.match(r'\A(coding)[\w]*@onefine.(top)\Z','coding@onefine.top').group()
Out[52]: 'coding@onefine.top'

In [53]:

正則表達式語法4:分組匹配

字符 匹配
| 匹配左右任意一個表達式
(ab) 括號中表達式做爲一個分組
\<number> 引用編號爲number的分組匹配到的字符串,編號從1開始
(?P<name>) 分組起一個別名name
(?P=name) 引用別名爲name的分組匹配字符串
In [54]: re.match(r'abc|d','d').group()
Out[54]: 'd'

In [55]: re.match(r'abc|d','abc').group()
Out[55]: 'abc'

In [56]: re.match(r'abc|d','c').group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-56-dfcd4a662ebe> in <module>
----> 1 re.match(r'abc|d','c').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [57]:

匹配0-100的字符串:

In [57]: re.match(r'[1-9]?\d$|100','100').group()
Out[57]: '100'

In [58]: re.match(r'[1-9]?\d$|100','99').group()
Out[58]: '99'

In [59]: re.match(r'[1-9]?\d$|100','1').group()
Out[59]: '1'

In [60]: re.match(r'[1-9]?\d$|100','0').group()
Out[60]: '0'

In [61]:

匹配多郵箱:

n [63]: re.match(r'[\w]{4,8}@(163|126).com','onefine@126.com').group()
Out[63]: 'onefine@126.com'

In [64]: re.match(r'[\w]{4,8}@(163|126).com','onefine@163.com').group()
Out[64]: 'onefine@163.com'

In [65]: re.match(r'[\w]{4,8}@(163|126).com','onefine@963.com').group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-65-afd701da2fb6> in <module>
----> 1 re.match(r'[\w]{4,8}@(163|126).com','onefine@963.com').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [66]:

XML–引用分組

In [80]: re.match(r'<([\w]+>)[\w]+<\\\1','<img>onefine<\img>').group()
Out[80]: '<img>onefine<\\img>'

In [81]: re.match(r'<([\w]+>)[\w]+</\1','<img>onefine</img>').group()
Out[81]: '<img>onefine</img>'

In [82]:

80寫錯了,注意上面的\\是轉義\
正確的是81,可是有了新的發現:

正則轉義

先看個例子:

import re 

string = '3\8' 
m = re.search('(\d+)\\\\', string) 

if m is not None: 
print m.group(1) # 結果爲:3 

n = re.search(r'(\d+)\\', string) 

if n is not None: 
print n.group(1) # 結果爲:3

正則表達式字符串須要通過兩次轉義,這兩次分別是上面的「字符串轉義」和「正則轉義」,我的認爲「字符串轉義」必定先於「正則轉義」。

1)'\\\\'的過程:
先進行「字符串轉義」,前兩個反斜槓和後兩個反斜槓分別被轉義成了一個反斜槓;即「\\|\\」被轉成了「\|\」(「|」爲方便看清,請自動忽略)。「字符串轉義」後立刻進行「正則轉義」,「\\」被轉義爲了「\」,表示該正則式須要匹配一個反斜槓。

2)r'\\'的過程:
因爲原始字符串中全部字符直接按照字面意思來使用,不轉義特殊字符,故不作「字符串轉義」,直接進入第二步「正則轉義」,在正則轉義中「\\」被轉義爲了「\」,表示該正則式須要匹配一個反斜槓。

結論:也就是說原始字符串(即r'...')與「正則轉義」毫無關係,原始字符串僅在「字符串轉義」中起做用,使字符串免去一次轉義。

分組起別名&引用,即最後兩個成對使用:

In [97]: re.match(r'<(?P<mark>[\w]+>)[\w]+</(?P=mark)','<img>onefine</img>').group()
Out[97]: '<img>onefine</img>'

re模塊相關方法使用

模塊re中一些重要的函數

函數 描述
compile(pattern[, flags]) 根據包含正則表達式的字符串建立模式對象
search(pattern, string[,flags]) 在字符串中查找模式
match(pattern, string[,flags]) 在字符串開頭匹配模式
split(pattern, string[,maxsplit=0]) 根據模式來分割字符串
findall(pattern, string) 返回一個列表, 其中包含字符串中全部與模式匹配的子串
sub(pat, repl, string[,count=0]) 將字符串中與模式pat匹配的子串都替換爲repl
escape(string) 對字符串中全部的正則表達式特殊字符都進行轉義
C:\Users\ONEFINE>ipython
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 14:57:15) [MSC v.1915 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import re

In [2]: help(re.match)
Help on function match in module re:

match(pattern, string, flags=0)
    Try to apply the pattern at the start of the string, returning
    a Match object, or None if no match was found.
In [3]: help(re.search)
Help on function search in module re:

search(pattern, string, flags=0)
    Scan through string looking for a match to the pattern, returning
    a Match object, or None if no match was found.

search()方法:

  • 掃描字符串string,查找模式pattern指定的正則表達式模式,產生匹配的第一個位置,返回相應的Match對象實例,若是字符串中沒有位置與模式匹配,則返回None。請注意,這與在字符串中的某處找到零長度匹配不一樣。
In [4]: help(re.findall)
Help on function findall in module re:

findall(pattern, string, flags=0)
    Return a list of all non-overlapping matches in the string.

    If one or more capturing groups are present in the pattern, return
    a list of groups; this will be a list of tuples if the pattern
    has more than one group.

    Empty matches are included in the result.

findall()方法

  • 返回的全部非重疊的匹配模式 的字符串,如字符串列表。該字符串進行掃描左到右,並匹配以發現的順序返回。若是模式中存在一個或多個組,返回組列表; 若是模式有多個組,這將是一個元組列表。空結果包含在結果中,除非他們觸及另外一場比賽的開始。

例子:

In [15]: re.search(r':\d+','Python3.x:4000 C++:5000 JAVA:4800').group()[1:]
Out[15]: '4000'

In [18]: re.findall(r':\d+','Python3.x:4000 C++:5000 JAVA:4800')
Out[18]: [':4000', ':5000', ':4800']

In [19]:
In [5]: help(re.compile)
Help on function compile in module re:

compile(pattern, flags=0)
    Compile a regular expression pattern, returning a Pattern object.
In [6]: help(re.split)
Help on function split in module re:

split(pattern, string, maxsplit=0, flags=0)
    Split the source string by the occurrences of the pattern,
    returning a list containing the resulting substrings.  If
    capturing parentheses are used in pattern, then the text of all
    groups in the pattern are also returned as part of the resulting
    list.  If maxsplit is nonzero, at most maxsplit splits occur,
    and the remainder of the string is returned as the final element
    of the list.

split()方法

  • 根據模式的出現拆分字符串。若是在模式中使用捕獲括號,則模式中全部組的文本也會做爲結果列表的一部分返回。
  • 若是maxsplit不爲零,則最多發生maxsplit分割,而且字符串的其他部分做爲列表的最後一個元素返回。

例子:

In [50]: str_2 = "course: Python C C++ C# JAVA,etc"

In [51]: re.split(r':| |,',str_2)
Out[51]: ['course', '', 'Python', 'C', 'C++', 'C#', 'JAVA', 'etc']

In [52]: re.split(r' :| |,',str_2)
Out[52]: ['course:', 'Python', 'C', 'C++', 'C#', 'JAVA', 'etc']

In [53]:
In [7]: help(re.sub)
Help on function sub in module re:

sub(pattern, repl, string, count=0, flags=0)
    Return the string obtained by replacing the leftmost
    non-overlapping occurrences of the pattern in string by the
    replacement repl.  repl can be either a string or a callable;
    if a string, backslash escapes in it are processed.  If it is
    a callable, it's passed the Match object and must return
    a replacement string to be used.

sub()方法

  • 經過用替換repl替換字符串中最左邊不重疊出現的模式而得到的字符串。 若是未找到該模式,則字符串將保持不變。 repl能夠是一個字符串或一個函數; 若是它是一個字符串,則處理其中的任何反斜槓轉義。 也就是\ n被轉換爲單個換行符,\ r被轉換爲回車符,等等。 像\ j這樣的未知轉義字符將被單獨保留。 反向引用(例如\ 6)被替換爲模式中由組6匹配的子字符串。

repl爲字符串的例子:

In [24]: str = "course Python videonum: 1000"

In [25]: re.sub(r'\d+','9631', str)
Out[25]: 'course Python videonum: 9631'

In [26]:

與分組一塊兒用:

In [23]: re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',r'static PyObject*\npy_\1(void)\n{','def myfunc():')
Out[23]: 'static PyObject*\npy_myfunc(void)\n{'

repl爲函數的例子:

In [43]: def add(match):
    ...:     val = match.group()
    ...:     num = int(val) + 1
    ...:     return str(num)
    ...:
    ...:

In [44]: str
Out[44]: 'course Python videonum: 1000'

In [45]: del(str)

In [46]: str_1 = 'course Python videonum: 1000'

In [48]: re.sub(r'\d+', add, str_1)
Out[48]: 'course Python videonum: 1001'

In [49]:

提示:str是Python內置函數。千萬不要定義爲變量名。錯誤TypeError: ‘str’ object is not callable字面上意思:就是str不能夠被系統調用,其實緣由就是:你正在調用一個不能被調用的變量或對象,具體表現就是你調用函數、變量的方式錯誤。

In [8]: help(re.escape)
Help on function escape in module re:

escape(pattern)
    Escape special characters in a string.


In [10]: help(re.I)
Help on RegexFlag in module re object:

class RegexFlag(enum.IntFlag)
 |  RegexFlag(value, names=None, *, module=None, qualname=None, type=None, start=1)
 |
 |  An enumeration.
 |
 |  Method resolution order:
 |      RegexFlag
 |      enum.IntFlag
 |      builtins.int
 |      enum.Flag
 |      enum.Enum
 |      builtins.object
 |
 |  Data and other attributes defined here:
 |
 |  ASCII = <RegexFlag.ASCII: 256>
 |
 |  DEBUG = <RegexFlag.DEBUG: 128>
 |
 |  DOTALL = <RegexFlag.DOTALL: 16>
 |
 |  IGNORECASE = <RegexFlag.IGNORECASE: 2>
 |
 |  LOCALE = <RegexFlag.LOCALE: 4>
 |
 |  MULTILINE = <RegexFlag.MULTILINE: 8>
 |
 |  TEMPLATE = <RegexFlag.TEMPLATE: 1>
 |
 |  UNICODE = <RegexFlag.UNICODE: 32>
 |
 |  VERBOSE = <RegexFlag.VERBOSE: 64>
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from enum.Enum:
 |
 |  name
 |      The name of the Enum member.
 |
 |  value
 |      The value of the Enum member.
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from enum.EnumMeta:
 |
 |  __members__
 |      Returns a mapping of member name->value.
 |
 |      This mapping lists all enum members, including aliases. Note that this
 |      is a read-only view of the internal mapping.


In [11]:

Flags標誌符

正則表達式能夠包含一些標誌修飾符來控制匹配模式,用在正則表達式處理函數中的flag參數中,爲可選參數。

標誌 描述
re.I(IGNORECASE) 忽略大小寫
re.M(MULTILINE) 多行模式,改變’^‘和’$'的行爲
re.S(DOTALL) 改變’.'的行爲
re.X(VERBOSE) 能夠給你的表達式寫註釋

注:

  • 除以上標誌外還有re.L和re.U,但不經常使用
  • 能夠經過使用運算符「|「來指定多個標誌,表示同時生效。如
>ipython
In [1]: str_1 = 'Hello world'
In [2]: import re

In [3]: re.search(r'wo..d', str_1, re.I|re.M).group()
Out[3]: 'world'

In [4]:
re.I(re.IGNORECASE): 表示使匹配時,忽略大小寫
In [1]: import re

In [2]: re.search('a','abc').group()
Out[2]: 'a'

In [3]: re.search('a','Abc').group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-94da83bf8997> in <module>
----> 1 re.search('a','Abc').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [4]: re.search('a','Abc',re.I).group()
Out[4]: 'A'

In [5]:
M(MULTILINE): 多行模式,影響(改變)^$的行爲
In [6]: re.search('foo.$', 'foo2\nfoo3\n').group()
Out[6]: 'foo3'

In [7]: re.search('foo.$', 'foo2\nfoo3\n', re.M).group()
Out[7]: 'foo2'

In [8]:

疑問:$不包括換行??? 加了M以後匹配第一行的結尾

S(DOTALL): 影響.的行爲,使點.匹配包括換行在內的全部字符
- make the '.' special character match any character at all, including a newline; without flag, '.' will match anything except a newline.
In [15]: re.search('.', '\n').group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-15-d4cd05f33b46> in <module>
----> 1 re.search('.', '\n').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [16]: re.search('.', '\n', re.S).group()
Out[16]: '\n'

In [17]:

注:加S能夠匹配包括換行符在內的全部字符

X(re.VERBOSE): 這個模式下正則表達式能夠是多行,忽略空白字符,並能夠加入註釋,使其更可讀。
In [22]: re.search('. # test', 'onefine', re.X).group()
Out[22]: 'o'

In [23]: re.search('.# test', 'onefine', re.X).group()
Out[23]: 'o'

In [24]: re.search('. # test', 'onefine', re.X).group()
Out[24]: 'o'

In [25]:  re.search('. # test', 'onefine').group()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-25-bffa2d2d5cfc> in <module>
----> 1 re.search('. # test', 'onefine').group()

AttributeError: 'NoneType' object has no attribute 'group'

In [26]:  re.search('. # test', 'o # test haha').group()
Out[26]: 'o # test'

In [27]:

注:

  • #前面的空格都無效
  • 當該標誌被指定時,在 RE 字符串中的空白符被忽略,除非該空白符在字符類中或在反斜槓以後。
  • 它也能夠容許你將註釋寫入 RE,這些註釋會被引擎忽略;
  • 註釋用 #號 來標識,不過該符號不能在字符串或反斜槓以後。


綜合
line = "XXX出生於1996年6月"
line = "XXX出生於1996/6/1"
line = "XXX出生於1996-6-1"
line = "XXX出生於1996-06-01"
line = "XXX出生於1996-06"
regex_str = ".*出生於(\d{4}[年/-]\d{1,2}([月/-]\d{1,2}|[月/-]$|$))"
相關文章
相關標籤/搜索