re模塊

1、正則表達式

正則表達式自己是一種小型的、高度專業化的編程語言,它並非Python的一部分。正則表達式是用於處理字符串的強大工具,擁有本身獨特的語法以及一個獨立的處理引擎,效率上可能不如str自帶的方法,但功能十分強大。得益於這一點,在提供了正則表達式的語言裏,正則表達式的語法都是同樣的,區別只在於不一樣的編程語言實現支持的語法數量不一樣;但不用擔憂,不被支持的語法一般是不經常使用的部分。若是已經在其餘語言裏使用過正則表達式,只須要簡單看一看就能夠上手了。而在python中,經過內嵌集成re模塊,程序員們能夠直接調用來實現正則匹配。正則表達式模式被編譯成一系列的字節碼,而後由用C編寫的匹配引擎執行。程序員

下圖展現了使用正則表達式進行匹配的流程:正則表達式

80re模塊-流程圖.png?x-oss-process=style/watermark

正則表達式的大體匹配過程是:依次拿出表達式和文本中的字符比較,若是每個字符都能匹配,則匹配成功;一旦有匹配不成功的字符則匹配失敗。若是表達式中有量詞或邊界,這個過程會稍微有一些不一樣,但也是很好理解的,看下圖中的示例以及本身多使用幾回就能明白。express

下圖列出了Python支持的正則表達式元字符和語法:編程

80re模塊-匹配圖.png?x-oss-process=style/watermark

1.1 數量詞的貪婪模式與非貪婪模式

正則表達式一般用於在文本中查找匹配的字符串。Python裏數量詞默認是貪婪的(在少數語言裏也多是默認非貪婪),老是嘗試匹配儘量多的字符;非貪婪的則相反,老是嘗試匹配儘量少的字符。例如:正則表達式"ab"若是用於查找"abbbc",將找到"abbb"。而若是使用非貪婪的數量詞"ab?",將找到"a"。編程語言

1.2 反斜槓的困擾

與大多數編程語言相同,正則表達式裏使用\做爲轉義字符,這就可能形成反斜槓困擾。假如你須要匹配文本中的字符\,那麼使用編程語言表示的正則表達式裏將須要4個反斜槓\\\\:前兩個和後兩個分別用於在編程語言裏轉義成反斜槓,轉換成兩個反斜槓後再在正則表達式裏轉義成一個反斜槓。Python裏的原生字符串很好地解決了這個問題,這個例子中的正則表達式可使用r'\\'表示。一樣,匹配一個數字的\\d能夠寫成r'\d'。有了原生字符串,你不再用擔憂是否是漏寫了反斜槓,寫出來的表達式也更直觀。函數

1.3 匹配模式

正則表達式提供了一些可用的匹配模式,好比忽略大小寫、多行匹配等,這部份內容將在Pattern類的工廠方法re.compile(pattern[, flags])中一塊兒介紹。工具

2、re模塊的基本使用

正則表達式是用來匹配處理字符串的 python 中使用正則表達式須要引入re模塊spa

import re  # 第一步,要引入re模塊
a = re.findall("匹配規則", "這個字符串是否有匹配規則的字符")  # 第二步,調用模塊函數
print(a)  # 以列表形式返回匹配到的字符串
['匹配規則']

^元字符翻譯

字符串開始位置與匹配規則符合就匹配,不然不匹配

匹配字符串開頭。在多行模式中匹配每一行的開頭(Python3+已經失效,配合compile使用)

^元字符若是寫到[]字符集裏就是反取

import re
a = re.findall("^匹配規則", "匹配規則這個字符串是否匹配")  # 字符串開始位置與匹配規則符合就匹配,不然不匹配
print(a)
#打印出 ['匹配規則']
['匹配規則']

[^a-z]反取

匹配出除字母外的字符,^元字符若是寫到字符集裏就是反取

import re
a = re.findall("[^a-z]", "匹配s規則這s個字符串是否s匹配f規則則re則則則")  # 反取,匹配出除字母外的字符
print(a)
['匹', '配', '規', '則', '這', '個', '字', '符', '串', '是', '否', '匹', '配', '規', '則', '則', '則', '則', '則']

$元字符

字符串結束位置與匹配規則符合就匹配,不然不匹配

匹配字符串末尾,在多行模式中匹配每一行的末尾

import re
a = re.findall("匹配規則$", "這個字符串是否匹配規則")  # 字符串結束位置與匹配規則符合就匹配,不然不匹配
print(a)
['匹配規則']

*元字符

須要字符串裏徹底符合,匹配規則,就匹配,(規則裏的*元字符)前面的一個字符能夠是0個或多個本來字符

匹配前一個字符0或屢次,貪婪匹配前導字符有多少個就匹配多少個很貪婪

若是規則裏只有一個分組,儘可能避免用*不然會有可能匹配出空字符串

import re
# 須要字符串裏徹底符合,匹配規則,就匹配,(規則裏的*元字符)前面的一個字符能夠是0或多個本來字符
a = re.findall("匹配規則*", "這個字符串是否匹配規則則則則則")
print(a)
['匹配規則則則則則']

+元字符

須要字符串裏徹底符合,匹配規則,就匹配,(規則裏的+元字符)前面的一個字符能夠是1個或多個本來字符

匹配前一個字符1次或無限次,貪婪匹配前導字符有多少個就匹配多少個很貪婪

import re
# 須要字符串裏徹底符合,匹配規則,就匹配,(規則裏的+元字符)前面的一個字符能夠是1個或多個本來字符
a = re.findall("匹配+", "匹配配配配配規則這個字符串是否匹配規則則則則則")
print(a)
['匹配配配配配', '匹配']

?元字符(防止貪婪匹配)

須要字符串裏徹底符合,匹配規則,就匹配,(規則裏的?元字符)前面的一個字符能夠是0個或1個本來字符

匹配一個字符0次或1次

還有一個功能是能夠防止貪婪匹配,詳情見防貪婪匹配

import re
# 須要字符串裏徹底符合,匹配規則,就匹配,(規則裏的?元字符)前面的一個字符能夠是0個或1個本來字符
a = re.findall("匹配規則?", "匹配規這個字符串是否匹配規則則則則則")
print(a)
['匹配規', '匹配規則']

{}元字符(範圍)

須要字符串裏徹底符合,匹配規則,就匹配,(規則裏的 {} 元字符)前面的一個字符,是自定義字符數,位數的本來字符

{m}匹配前一個字符m次,{m,n}匹配前一個字符m至n次,若省略n,則匹配m至無限次

{0,}匹配前一個字符0或屢次,等同於*元字符
{+,}匹配前一個字符1次或無限次,等同於+元字符
{0,1}匹配前一個字符0次或1次,等同於?元字符

import re
# {m}匹配前一個字符m次,{m,n}匹配前一個字符m至n次,若省略n,則匹配m至無限次
a = re.findall("匹配規則{3}", "匹配規這個字符串是否匹配規則則則則則")
print(a)
['匹配規則則則']

[]元字符(字符集)

須要字符串裏徹底符合,匹配規則,就匹配,(規則裏的 [] 元字符)對應位置是[]裏的任意一個字符就匹配

字符集。對應的位置能夠是字符集中任意字符。字符集中的字符能夠逐個列出,也能夠給出範圍,如[abc]或[a-c]。[^abc]表示取反,即非abc。
全部特殊字符在字符集中都失去其原有的特殊含義。用\反斜槓轉義恢復特殊字符的特殊含義。

import re
# 須要字符串裏徹底符合,匹配規則,就匹配,(規則裏的 [] 元字符)對應位置是[]裏的任意一個字符就匹配
a = re.findall("匹配[a,b,c]規則", "匹配a規則這個字符串是否匹配b規則則則則則")
print(a)
['匹配a規則', '匹配b規則']

[^]

非,反取,匹配出除[^]裏面的字符,^元字符若是寫到字符集裏就是反取

import re
a = re.findall("[^a-z]", "匹配s規則這s個字符串是否s匹配f規則則re則則則")  # 反取,匹配出除字母外的字符
print(a)
['匹', '配', '規', '則', '這', '個', '字', '符', '串', '是', '否', '匹', '配', '規', '則', '則', '則', '則', '則']

反斜槓後邊跟普通字符實現特殊功能(預約義字符)

預約義字符是在字符集和組裏都是有用的

\d匹配任何十進制數,它至關於類[0-9]

import re
a = re.findall("\d", "匹配規則這2個字符串3是否匹配規則5則則則7則")  # \d匹配任何十進制數,它至關於類[0-9]
print(a)
['2', '3', '5', '7']

\d+

匹配一位或者多位數的數字時用

import re
a = re.findall("\d+", "匹配規則這2個字符串134444是否匹配規則5則則則7則")  # \d+若是須要匹配一位或者多位數的數字時用
print(a)
['2', '134444', '5', '7']

\D

匹配任何非數字字符,它至關於類[^0-9]

import re
a = re.findall("\D", "匹配規則這2個字符串3是否匹配規則5則則則7則")  # \D匹配任何非數字字符,它至關於類[^0-9]
print(a)
['匹', '配', '規', '則', '這', '個', '字', '符', '串', '是', '否', '匹', '配', '規', '則', '則', '則', '則', '則']

\s

匹配任何空白字符,它至關於類[\t\n\r\f\v]

import re
# \s匹配任何空白字符,它至關於類[\t\n\r\f\v]
a = re.findall("\s", "匹配規則   這2個字符串3是否匹\n配規則5則則則7則")
print(a)
[' ', ' ', ' ', '\n']

\S

匹配任何非空白字符,它至關於類[^\t\n\r\f\v]

import re
# \S匹配任何非空白字符,它至關於類[^\t\n\r\f\v]
a = re.findall("\S", "匹配規則   這2個字符串3是否匹\n配規則5則則則7則")
print(a)
['匹', '配', '規', '則', '這', '2', '個', '字', '符', '串', '3', '是', '否', '匹', '配', '規', '則', '5', '則', '則', '則', '7', '則']

\w

匹配包括下劃線在內任何字母數字字符,它至關於類[a-zA-Z0-9_]

import re
# \w匹配包括下劃線在內任何字母數字字符,它至關於類[a-zA-Z0-9_]
a = re.findall('\w', "https://www.cnblogs.com/")
print(a)
['h', 't', 't', 'p', 's', 'w', 'w', 'w', 'c', 'n', 'b', 'l', 'o', 'g', 's', 'c', 'o', 'm']

\W

匹配非任何字母數字字符包括下劃線在內,它至關於類[^a-zA-Z0-9_]

import re
# \w匹配包括下劃線在內任何字母數字字符,它至關於類[a-zA-Z0-9_]
a = re.findall('\W', "https://www.cnblogs.com/")
print(a)
[':', '/', '/', '.', '.', '/']

()元字符(分組)

也就是分組匹配,()裏面的爲一個組也能夠理解成一個總體

若是()後面跟的是特殊元字符如 (adc)* 那麼*控制的前導字符就是()裏的總體內容,再也不是前導一個字符

import re
# 也就是分組匹配,()裏面的爲一個組也能夠理解成一個總體
a = re.search("(a4)+", "a4a4a4a4a4dg4g654gb")  # 匹配一個或多個a4
b = a.group()
print(b)
a4a4a4a4a4
import re
# 也就是分組匹配,()裏面的爲一個組也能夠理解成一個總體
# 匹配 (a) (\d0-9的數字) (+能夠是1個到多個0-9的數字)
a = re.search("a(\d+)", "a466666664a4a4a4dg4g654gb")
b = a.group()
print(b)
a466666664

|元字符(或)

|或,或就是先後其中一個符合就匹配

import re
a = re.findall(r"你|好", "a4a4a你4aabc4a4dgg好dg4g654g")  # |或,或就是先後其中一個符合就匹配
print(a)
['你', '好']

3、re模塊中經常使用功能函數

3.1 正則表達式的兩種書寫方式

1.一種是直接在函數裏書寫規則,推薦使用

import re
a = re.findall("匹配規則", "這個字符串是否有匹配規則的字符")
print(a)
['匹配規則']

2.另外一種是先將正則表達式的字符串形式編譯爲Pattern實例,而後使用Pattern實例處理文本並得到匹配結果(一個Match實例),最後使用Match實例得到信息,進行其餘的操做。

import re

# 將正則表達式編譯成Pattern對象
pattern = re.compile(r'hello')

# 使用Pattern匹配文本,得到匹配結果,沒法匹配時將返回None
match = pattern.match('hello world!')

if match:
    # 使用Match得到分組信息
    print(match.group())
hello

3.2 re.compile(strPattern[, flag])函數

這個方法是Pattern類的工廠方法,用於將字符串形式的正則表達式編譯爲Pattern對象。 第二個參數flag是匹配模式,取值可使用按位或運算符'|'表示同時生效,好比re.I | re.M。另外,你也能夠在regex字符串中指定模式,好比re.compile('pattern', re.I | re.M)與re.compile('(?im)pattern')是等價的。

下表是全部的正則匹配模式:

修飾符 描述
re.I 使匹配對大小寫不敏感
re.L 作本地化識別(locale-aware)匹配
re.M 多行匹配,影響 ^ 和 $
re.S 使 . 匹配包括換行在內的全部字符
re.U 根據Unicode字符集解析字符。這個標誌影響 \w, \W, \b, \B.
re.X 該標誌經過給予你更靈活的格式以便你將正則表達式寫得更易於理解。

3.2.1 re.S

  • 在Python的正則表達式中,有一個參數爲re.S。它表示 「.」 的做用擴展到整個字符串,包括「\n」。看以下代碼:
import re
a = '''asdfhellopass:
    worldaf
    '''
b = re.findall('hello(.*?)world', a)
c = re.findall('hello(.*?)world', a, re.S)
print('b is ', b)
print('c is ', c)
b is  []
c is  ['pass:\n    ']

正則表達式中,「.」的做用是匹配除「\n」之外的任何字符,也就是說,它是在一行中進行匹配。這裏的「行」是以「\n」進行區分的。a字符串有每行的末尾有一個「\n」,不過它不可見。

若是不使用re.S參數,則只在每一行內進行匹配,若是一行沒有,就換下一行從新開始,不會跨行。而使用re.S參數之後,正則表達式會將這個字符串做爲一個總體,將「\n」當作一個普通的字符加入到這個字符串中,在總體中進行匹配。

3.2.2 re.I

  • 不區分大小寫
res = re.findall(r"A", "abc", re.I)
print(res)
['a']

3.2.3 re.M

  • 將全部行的尾字母輸出(python3+已經無效)
s = '12 34/n56 78/n90'

re.findall(r'^/d+', s, re.M)  # 匹配位於行首的數字  # ['12', '56', '90']
re.findall(r'/A/d+', s, re.M)  # 匹配位於字符串開頭的數字  # ['12']
re.findall(r'/d+$', s, re.M)  # 匹配位於行尾的數字  # ['34', '78', '90']
re.findall(r'/d+/Z', s, re.M)  # 匹配位於字符串尾的數字  # ['90']

3.2.4 re.sub

# 要求結果:['12', '23', '34']
l = ['1 2 ', '2   3', '  3 4']
import re
print(eval(re.sub(r'\s*', '', str(l))))
['12', '23', '34']

3.3 re.match(pattern, string[, flags])函數(經常使用)

match,從頭匹配一個符合規則的字符串,從起始位置開始匹配,匹配成功返回一個對象,未匹配成功返回None
match(pattern, string, flags=0)

  • pattern: 正則模型
  • string : 要匹配的字符串
  • falgs : 匹配模式

注意:match()函數 與 search()函數基本是同樣的功能,不同的就是match()匹配字符串開始位置的一個符合規則的字符串,search()是在字符串全局匹配第一個合規則的字符串

import re
# 無分組
origin = "hello egon bcd egon lge egon acd 19"
r = re.match("h\w+", origin)  # match,從起始位置開始匹配,匹配成功返回一個對象,未匹配成功返回None
print(r.group())  # 獲取匹配到的全部結果,無論有沒有分組將匹配到的所有拿出來
print(r.groups())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果
print(r.groupdict())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果
hello
()
{}
# 有分組
# 爲什麼要有分組?提取匹配成功的指定內容(先匹配成功所有正則,再匹配成功的局部內容提取出來)
r = re.match("h(\w+)", origin)  # match,從起始位置開始匹配,匹配成功返回一個對象,未匹配成功返回None
print(r.group())  # 獲取匹配到的全部結果,無論有沒有分組將匹配到的所有拿出來
print(r.groups())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果
print(r.groupdict())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果
hello
('ello',)
{}
# 有兩個分組定義了key
# 爲什麼要有分組?提取匹配成功的指定內容(先匹配成功所有正則,再匹配成功的局部內容提取出來)
# ?P<>定義組裏匹配內容的key(鍵),<>裏面寫key名稱,值就是匹配到的內容
r = re.match("(?P<n1>h)(?P<n2>\w+)", origin)
print(r.group())  # 獲取匹配到的全部結果,無論有沒有分組將匹配到的所有拿出來
print(r.groups())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果
print(r.groupdict())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果
hello
('h', 'ello')
{'n1': 'h', 'n2': 'ello'}

3.4 分組函數

?P<n1> # ?P<>定義組裏匹配內容的key(鍵),<>裏面寫key名稱,值就是匹配到的內容(只對正則函數返回對象時有用)

取出匹配對象方法

只對正則函數返回對象的有用

  • group() # 獲取匹配到的全部結果,無論有沒有分組將匹配到的所有拿出來,有參取匹配到的第幾個如2
  • groups() # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果
  • groupdict() # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果

3.5 re.search(pattern, string[, flags])函數

search,瀏覽所有字符串,匹配第一符合規則的字符串,瀏覽整個字符串去匹配第一個,未匹配成功返回None

search(pattern, string, flags=0)

  • pattern: 正則模型
  • string : 要匹配的字符串
  • falgs : 匹配模式

注意:match()函數 與 search()函數基本是同樣的功能,不同的就是match()匹配字符串開始位置的一個符合規則的字符串,search()是在字符串全局匹配第一個合規則的字符串

import re
# 無分組
origin = "hello alex bcd alex lge alex acd 19"
# search瀏覽所有字符串,匹配第一符合規則的字符串,瀏覽整個字符串去匹配第一個,未匹配成功返回None
r = re.search("a\w+", origin)
print(r.group())  # 獲取匹配到的全部結果,無論有沒有分組將匹配到的所有拿出來
print(r.groups())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果
print(r.groupdict())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果
alex
()
{}
# 有分組
# 爲什麼要有分組?提取匹配成功的指定內容(先匹配成功所有正則,再匹配成功的局部內容提取出來)
r = re.search("a(\w+).*(\d)", origin)
print(r.group())  # 獲取匹配到的全部結果,無論有沒有分組將匹配到的所有拿出來
print(r.groups())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果
print(r.groupdict())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果
alex bcd alex lge alex acd 19
('lex', '9')
{}
# 有兩個分組定義了key
# 爲什麼要有分組?提取匹配成功的指定內容(先匹配成功所有正則,再匹配成功的局部內容提取出來)
# ?P<>定義組裏匹配內容的key(鍵),<>裏面寫key名稱,值就是匹配到的內容
r = re.search("a(?P<n1>\w+).*(?P<n2>\d)", origin)
print(r.group())  # 獲取匹配到的全部結果,無論有沒有分組將匹配到的所有拿出來
print(r.groups())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果
print(r.groupdict())  # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果
alex bcd alex lge alex acd 19
('lex', '9')
{'n1': 'lex', 'n2': '9'}

3.6 re.findall(pattern, string[, flags])函數(經常使用)

findall(pattern, string, flags=0)

  • pattern: 正則模型
  • string : 要匹配的字符串
  • falgs : 匹配模式

瀏覽所有字符串,匹配全部合規則的字符串,匹配到的字符串放到一個列表中,未匹配成功返回空列表

注意:一旦匹配成,再次匹配,是從前一次匹配成功的,後面一位開始的,也能夠理解爲匹配成功的字符串,不在參與下次匹配

import re
# 無分組
r = re.findall("\d+\w\d+", "a2b3c4d5")  # 瀏覽所有字符串,匹配全部合規則的字符串,匹配到的字符串放到一個列表中
print(r)  # 注意:匹配成功的字符串,不在參與下次匹配,因此3c4也符合規則可是沒匹配到
['2b3', '4d5']

注意:若是沒寫匹配規則,也就是空規則,返回的是一個比原始字符串多一位的,空字符串列表

import re
# 無分組
r = re.findall("", "a2b3c4d5")  # 瀏覽所有字符串,匹配全部合規則的字符串,匹配到的字符串放到一個列表中
print(r)  # 注意:若是沒寫匹配規則,也就是空規則,返回的是一個比原始字符串多一位的,空字符串列表
['', '', '', '', '', '', '', '', '']

注意:正則匹配到空字符的狀況,若是規則裏只有一個組,而組後面是就表示組裏的內容能夠是0個或者多過,這樣組裏就有了兩個意思,一個意思是匹配組裏的內容,二個意思是匹配組裏0內容(便是空白)因此儘可能避免用不然會有可能匹配出空字符串

注意:正則只拿組裏最後一位,若是規則裏只有一個組,匹配到的字符串裏在拿組內容是,拿的是匹配到的內容最後一位

import re
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("(a)*", origin)
print(r)
['', '', '', '', '', '', 'a', '', '', '', '', '', '', '', '', 'a', '', '', '', '', '', '', '', '', 'a', '', '', '', '', 'a', '', '', '', '', '', '']

無分組:匹配全部合規則的字符串,匹配到的字符串放到一個列表中

import re
# 無分組
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("a\w+", origin)  # 瀏覽所有字符串,匹配全部合規則的字符串,匹配到的字符串放到一個列表中
print(r)
['alex', 'alex', 'alex', 'acd']

有分組:只將匹配到的字符串裏,組的部分放到列表裏返回,至關於groups()方法

import re
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("a(\w+)", origin)  # 有分組:只將匹配到的字符串裏,組的部分放到列表裏返回
print(r)
['lex', 'lex', 'lex', 'cd']

多個分組:只將匹配到的字符串裏,組的部分放到一個元組中,最後將全部元組放到一個列表裏返

至關於在group()結果裏再將組的部分,分別,拿出來放入一個元組,最後將全部元組放入一個列表返回

import re
origin = "hello alex bcd alex lge alex acd 19"
# 多個分組:只將匹配到的字符串裏,組的部分放到一個元組中,最後將全部元組放到一個列表裏返回
r = re.findall("(a)(\w+)", origin)
print(r)
[('a', 'lex'), ('a', 'lex'), ('a', 'lex'), ('a', 'cd')]

分組中有分組:只將匹配到的字符串裏,組的部分放到一個元組中,先將包含有組的組,看做一個總體也就是一個組,把這個總體組放入一個元組裏,而後在把組裏的組放入一個元組,最後將全部組放入一個列表返回

import re
origin = "hello alex bcd alex lge alex acd 19"
# 分組中有分組:只將匹配到的字符串裏,組的部分放到一個元組中,先將包含有組的組,看做一個總體也就是一個組,把這個總體組放入一個元組裏,而後在把組裏的組放入一個元組,最後將全部組放入一個列表返回
r = re.findall("(a)(\w+(e))", origin)
print(r)
[('a', 'le', 'e'), ('a', 'le', 'e'), ('a', 'le', 'e')]

?:在有分組的狀況下findall()函數,不僅拿分組裏的字符串,拿全部匹配到的字符串,注意?:只用於不是返回正則對象的函數如findall()

import re
origin = "hello alex bcd alex lge alex acd 19"
# ?:在有分組的狀況下,不僅拿分組裏的字符串,拿全部匹配到的字符串,注意?:只用於不是返回正則對象的函數如findall()
b = re.findall("a(?:\w+)", origin)
print(b)
['alex', 'alex', 'alex', 'acd']

3.7 re.split(pattern, string[, maxsplit])函數

根據正則匹配分割字符串,返回分割後的一個列表

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

  • pattern: 正則模型
  • string : 要匹配的字符串
  • maxsplit:指定分割個數
  • flags : 匹配模式

按照一個字符將所有字符串進行分割

import re
origin = "hello alex bcd alex lge alex acd 19"
r = re.split("a", origin)  # 根據正則匹配分割字符串
print(r)
['hello ', 'lex bcd ', 'lex lge ', 'lex ', 'cd 19']

將匹配到的字符串做爲分割標準進行分割

import re
origin = "hello alex bcd alex lge alex 2acd 19"
r = re.split("a\w+", origin)  # 根據正則匹配分割字符串
print(r)
['hello ', ' bcd ', ' lge ', ' 2', ' 19']

3.8 re.sub(pattern, repl, string[, count])函數

替換匹配成功的指定位置字符串

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

  • pattern: 正則模型
  • repl : 要替換的字符串
  • string : 要匹配的字符串
  • count : 指定匹配個數
  • flags : 匹配模式
import re
origin = "hello alex bcd alex lge alex acd 19"
r = re.sub("a", "替換", origin)  # 替換匹配成功的指定位置字符串
print(r)
hello 替換lex bcd 替換lex lge 替換lex 替換cd 19

3.9 re.subn(pattern, repl, string,[, count][, flags])函數

替換匹配成功的指定位置字符串,而且返回替換次數,能夠用兩個變量分別接受

subn(pattern, repl, string, count=0, flags=0)

  • pattern: 正則模型
  • repl : 要替換的字符串
  • string : 要匹配的字符串
  • count : 指定匹配個數
  • flags : 匹配模式
import re
origin = "hello alex bcd alex lge alex acd 19"
a, b = re.subn("a", "替換", origin)  # 替換匹配成功的指定位置字符串,而且返回替換次數,能夠用兩個變量分別接受
print(a)
print(b)
hello 替換lex bcd 替換lex lge 替換lex 替換cd 19
4

4、注意事項

  1. r原生字符:讓在python裏有特殊意義的字符如\b,轉換成原生字符(就是去除它在python的特殊意義),否則會給正則表達式有衝突,爲了不這種衝突能夠在規則前加原始字符r
  2. 正則表達式,返回類型爲表達式對象的,如:<_sre.SRE_Match object; span=(6, 7), match='a'>,返回對象時,須要用正則方法取字符串,方法有:
    1. group() # 獲取匹配到的全部結果,無論有沒有分組將匹配到的所有拿出來,有參取匹配到的第幾個如2
    2. groups() # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果
    3. groupdict() # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果
  3. 匹配到的字符串裏出現空字符:注意:正則匹配到空字符的狀況,若是規則裏只有一個組,而組後面是*就表示組裏的內容能夠是0個或者多過,這樣組裏就有了兩個意思,一個意思是匹配組裏的內容,二個意思是匹配組裏0內容(便是空白)因此儘可能避免用*不然會有可能匹配出空字符串
  4. ()分組:注意:分組的意義,就是在匹配成功的字符串中,再提取()裏的內容,也就是組裏面的字符串
  5. ?:在有分組的狀況下findall()函數,不僅拿分組裏的字符串,拿全部匹配到的字符串,注意?:只用於不是返回正則對象的函數如findall()

5、計算器(經典)

基於遞歸和正則將下面的字符串翻譯成計算器表達式,而且獲取最終結果:expression='-1-2*((60+2*(-3-40.0+42425/5)*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)'

若是代碼正確,計算結果爲:-553071849.7670887

提示:content=re.search('\(([\-\+\*\/]*\d+\.?\d*)+\)',expression).group() #(-3-40.0/5)

5.1 複雜版本

#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
該計算器思路:
    一、遞歸尋找表達式中只含有 數字和運算符的表達式,並計算結果
    二、因爲整數計算會忽略小數,全部的數字都認爲是浮點型操做,以此來保留小數
使用技術:
    一、正則表達式
    二、遞歸
"""

import re


def compute_mul_div(arg):
    """ 操做乘除
    :param expression:表達式
    :return:計算結果
    """

    val = arg[0]
    mch = re.search('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*', val)
    if not mch:
        return
    content = re.search('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*', val).group()

    if len(content.split('*')) > 1:
        n1, n2 = content.split('*')
        value = float(n1) * float(n2)
    else:
        n1, n2 = content.split('/')
        value = float(n1) / float(n2)

    before, after = re.split('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*', val, 1)
    new_str = "%s%s%s" % (before, value, after)
    arg[0] = new_str
    compute_mul_div(arg)


def compute_add_sub(arg):
    """ 操做加減
    :param expression:表達式
    :return:計算結果
    """
    while True:
        if arg[0].__contains__('+-') or arg[0].__contains__("++") or arg[
                0].__contains__('-+') or arg[0].__contains__("--"):
            arg[0] = arg[0].replace('+-', '-')
            arg[0] = arg[0].replace('++', '+')
            arg[0] = arg[0].replace('-+', '-')
            arg[0] = arg[0].replace('--', '+')
        else:
            break

    if arg[0].startswith('-'):
        arg[1] += 1
        arg[0] = arg[0].replace('-', '&')
        arg[0] = arg[0].replace('+', '-')
        arg[0] = arg[0].replace('&', '+')
        arg[0] = arg[0][1:]
    val = arg[0]
    mch = re.search('\d+\.*\d*[\+\-]{1}\d+\.*\d*', val)
    if not mch:
        return
    content = re.search('\d+\.*\d*[\+\-]{1}\d+\.*\d*', val).group()
    if len(content.split('+')) > 1:
        n1, n2 = content.split('+')
        value = float(n1) + float(n2)
    else:
        n1, n2 = content.split('-')
        value = float(n1) - float(n2)

    before, after = re.split('\d+\.*\d*[\+\-]{1}\d+\.*\d*', val, 1)
    new_str = "%s%s%s" % (before, value, after)
    arg[0] = new_str
    compute_add_sub(arg)


def compute(expression):
    """ 操做加減乘除
    :param expression:表達式
    :return:計算結果
    """
    inp = [expression, 0]

    # 處理表達式中的乘除
    compute_mul_div(inp)

    # 處理
    compute_add_sub(inp)
    if divmod(inp[1], 2)[1] == 1:
        result = float(inp[0])
        result = result * -1
    else:
        result = float(inp[0])
    return result


def exec_bracket(expression):
    """ 遞歸處理括號,並計算
    :param expression: 表達式
    :return:最終計算結果
    """
    # 若是表達式中已經沒有括號,則直接調用負責計算的函數,將表達式結果返回,如:2*1-82+444
    if not re.search('\(([\+\-\*\/]*\d+\.*\d*){2,}\)', expression):
        final = compute(expression)
        return final
    # 獲取 第一個 只含有 數字/小數 和 操做符 的括號
    # 如:
    #    ['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
    #    找出:(-40.0/5)
    content = re.search('\(([\+\-\*\/]*\d+\.*\d*){2,}\)', expression).group()

    # 分割表達式,即:
    # 將['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
    # 分割更三部分:['1-2*((60-30+(    (-40.0/5)      *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
    before, nothing, after = re.split('\(([\+\-\*\/]*\d+\.*\d*){2,}\)',
                                      expression, 1)

    print('before:', expression)
    content = content[1:len(content) - 1]

    # 計算,提取的表示 (-40.0/5),並活的結果,即:-40.0/5=-8.0
    ret = compute(content)

    print('%s=%s' % (content, ret))

    # 將執行結果拼接,['1-2*((60-30+(      -8.0     *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
    expression = "%s%s%s" % (before, ret, after)
    print('after:', expression)
    print("=" * 10, '上一次計算結束', "=" * 10)

    # 循環繼續下次括號處理操做,本次攜帶者的是已被處理後的表達式,即:
    # ['1-2*((60-30+   -8.0  *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']

    # 如此周而復始的操做,直到表達式中再也不含有括號
    return exec_bracket(expression)


# 使用 __name__ 的目的:
# 只有執行 python index.py 時,如下代碼才執行
# 若是其餘人導入該模塊,如下代碼不執行
if __name__ == "__main__":
    print(
        '*' * 20, "請計算表達式:",
        "1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )",
        '*' * 20)
    #     inpp = '1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) '
    inpp = '-1-2*((60+2*(-3-40.0+42425/5)*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)'
    # inpp = "1-2*-30/-12*(-20+200*-3/-200*-300-100)"
    # inpp = "1-5*980.0"
    inpp = re.sub('\s*', '', inpp)
    # 表達式保存在列表中
    result = exec_bracket(inpp)
    print(result)
******************** 請計算表達式: 1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) ********************
before: -1-2*((60+2*(-3-40.0+42425/5)*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)
-3-40.0+42425/5=8442.0
after: -1-2*((60+2*8442.0*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)
========== 上一次計算結束 ==========
before: -1-2*((60+2*8442.0*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)
9-2*5/3+357/553/3*99/4*2998+10*568/14=16378.577154912598
after: -1-2*((60+2*8442.0*16378.577154912598)-(-4*3)/(16-3*2))+56+(56-45)
========== 上一次計算結束 ==========
before: -1-2*((60+2*8442.0*16378.577154912598)-(-4*3)/(16-3*2))+56+(56-45)
60+2*8442.0*16378.577154912598=276535956.68354434
after: -1-2*(276535956.68354434-(-4*3)/(16-3*2))+56+(56-45)
========== 上一次計算結束 ==========
before: -1-2*(276535956.68354434-(-4*3)/(16-3*2))+56+(56-45)
-4*3=-12.0
after: -1-2*(276535956.68354434--12.0/(16-3*2))+56+(56-45)
========== 上一次計算結束 ==========
before: -1-2*(276535956.68354434--12.0/(16-3*2))+56+(56-45)
16-3*2=10.0
after: -1-2*(276535956.68354434--12.0/10.0)+56+(56-45)
========== 上一次計算結束 ==========
before: -1-2*(276535956.68354434--12.0/10.0)+56+(56-45)
276535956.68354434--12.0/10.0=276535957.8835443
after: -1-2*276535957.8835443+56+(56-45)
========== 上一次計算結束 ==========
before: -1-2*276535957.8835443+56+(56-45)
56-45=11.0
after: -1-2*276535957.8835443+56+11.0
========== 上一次計算結束 ==========
-553071849.7670887

5.2 簡單易懂版

import re

expression = '-1-2*((60+2*(-3-40.0+42425/5)*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)'
question = eval(expression)
print(question)


def arithmetic(expression='1+1'):
    #     content = re.search('\(([\-\+\*\/]*\d+\.?\d*)+\)', expression)  # (-3-40.0/5)
    content = re.search('\(([-+*/]*\d+\.?\d*)+\)', expression)  # (-3-40.0/5)
    if content:
        content = content.group()
        content = content[1:-1]
        print('content:', content)
        replace_content = next_arithmetic(content)
        expression = re.sub('\(([-+*/]*\d+\.?\d*)+\)',
                            replace_content,
                            expression,
                            count=1)
        print('next_expression:', expression)
    else:
        answer = next_arithmetic(expression)
        return answer
    return arithmetic(expression)


def next_arithmetic(content):
    while True:
        next_content_mul_div = re.search('\d+\.?\d*[*/][-+]?\d+\.?\d*',
                                         content)  # 找出帶有*/的式子
        if next_content_mul_div:  # 若是content含有帶有*/的式子
            next_content_mul_div = next_content_mul_div.group()
            print('next_content_mul_div:', next_content_mul_div)
            mul_div_content = mul_div(next_content_mul_div)  # 計算出帶有*/的式子
            print('mul_div_content:', mul_div_content)
            content = re.sub('\d+\.?\d*[*/][-+]?\d+\.?\d*',
                             str(mul_div_content),
                             content,
                             count=1)  # 把帶有*/的式子計算出來後替換掉
            print('content:', content)
            continue
        next_content_add_sub = re.search('-?\d+\.?\d*[-+][-+]?\d+\.?\d*',
                                         content)  # 找出帶有-+的式子
        if next_content_add_sub:  # 若是content含有帶有+-的式子
            next_content_add_sub = next_content_add_sub.group()
            print('next_content_add_sub:', next_content_add_sub)
            add_sub_content = add_sub(next_content_add_sub)  # 計算出帶有-+的式子
            print('add_sub_content:', add_sub_content)
            add_sub_content = str(add_sub_content)
            content = re.sub('-?\d+\.?\d*[-+]-?\d+\.?\d*',
                             str(add_sub_content),
                             content,
                             count=1)  # 把帶有-+的式子計算出來後替換掉
            print('content:', content)
            continue
        else:
            break
    return content


def add_sub(content):
    if '+' in content:
        content = content.split('+')
        print(content)
        content = float(content[0]) + float(content[1])
        return content
    elif '-' in content:
        content = content.split('-')
        # 減法狀況有多種
        if content[0] == '-' and content[2] == '-':
            # content = content.split('-')
            print(content)
            content = -float(content[1]) - float(content[-1])
            return content
        if content[0] == '-':
            # content = content.split('-')
            print(content)
            content = -float(content[1]) - float(content[-1])
            return content
        if content[1] == '-' and content[2] == '-':
            # content = content.split('-')
            print(content)
            content = -float(content[0]) + float(content[-1])
            return content
        if content[1] == '':
            # content = content.split('-')
            print(content)
            content = float(content[0]) - float(content[2])
            return content
        if content[0] == '' and content[2] != '':
            print(content)
            content = -float(content[1]) - float(content[2])
            return content
        if content[0] == '' and content[2] == '':
            print(content)
            content = -float(content[1]) + float(content[3])
            return content
        else:
            # content = content.split('-')
            print(content)
            content = float(content[0]) - float(content[1])
            return content


def mul_div(content):
    if '*' in content:
        content = content.split('*')
        print(content)
        content = float(content[0]) * float(content[1])
        return content
    elif '/' in content:
        content = content.split('/')
        print(content)
        content = float(content[0]) / float(content[1])
        return content


# expression = '1-2*((60+2*(-3-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'
expression = '-1-2*((60+2*(-3-40.0+42425/5)*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)'
answer = arithmetic(expression)
print(answer)
-553071849.7670887
content: -3-40.0+42425/5
next_content_mul_div: 42425/5
['42425', '5']
mul_div_content: 8485.0
content: -3-40.0+8485.0
next_content_add_sub: -3-40.0
['', '3', '40.0']
add_sub_content: -43.0
content: -43.0+8485.0
next_content_add_sub: -43.0+8485.0
['-43.0', '8485.0']
add_sub_content: 8442.0
content: 8442.0
next_expression: -1-2*((60+2*8442.0*(9-2*5/3+357/553/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))+56+(56-45)
content: 9-2*5/3+357/553/3*99/4*2998+10*568/14
next_content_mul_div: 2*5
['2', '5']
mul_div_content: 10.0
content: 9-10.0/3+357/553/3*99/4*2998+10*568/14
next_content_mul_div: 10.0/3
['10.0', '3']
mul_div_content: 3.3333333333333335
content: 9-3.3333333333333335+357/553/3*99/4*2998+10*568/14
next_content_mul_div: 357/553
['357', '553']
mul_div_content: 0.6455696202531646
content: 9-3.3333333333333335+0.6455696202531646/3*99/4*2998+10*568/14
next_content_mul_div: 0.6455696202531646/3
['0.6455696202531646', '3']
mul_div_content: 0.21518987341772153
content: 9-3.3333333333333335+0.21518987341772153*99/4*2998+10*568/14
next_content_mul_div: 0.21518987341772153*99
['0.21518987341772153', '99']
mul_div_content: 21.303797468354432
content: 9-3.3333333333333335+21.303797468354432/4*2998+10*568/14
next_content_mul_div: 21.303797468354432/4
['21.303797468354432', '4']
mul_div_content: 5.325949367088608
content: 9-3.3333333333333335+5.325949367088608*2998+10*568/14
next_content_mul_div: 5.325949367088608*2998
['5.325949367088608', '2998']
mul_div_content: 15967.196202531646
content: 9-3.3333333333333335+15967.196202531646+10*568/14
next_content_mul_div: 10*568
['10', '568']
mul_div_content: 5680.0
content: 9-3.3333333333333335+15967.196202531646+5680.0/14
next_content_mul_div: 5680.0/14
['5680.0', '14']
mul_div_content: 405.7142857142857
content: 9-3.3333333333333335+15967.196202531646+405.7142857142857
next_content_add_sub: 9-3.3333333333333335
['9', '3.3333333333333335']
add_sub_content: 5.666666666666666
content: 5.666666666666666+15967.196202531646+405.7142857142857
next_content_add_sub: 5.666666666666666+15967.196202531646
['5.666666666666666', '15967.196202531646']
add_sub_content: 15972.862869198312
content: 15972.862869198312+405.7142857142857
next_content_add_sub: 15972.862869198312+405.7142857142857
['15972.862869198312', '405.7142857142857']
add_sub_content: 16378.577154912598
content: 16378.577154912598
next_expression: -1-2*((60+2*8442.0*16378.577154912598)-(-4*3)/(16-3*2))+56+(56-45)
content: 60+2*8442.0*16378.577154912598
next_content_mul_div: 2*8442.0
['2', '8442.0']
mul_div_content: 16884.0
content: 60+16884.0*16378.577154912598
next_content_mul_div: 16884.0*16378.577154912598
['16884.0', '16378.577154912598']
mul_div_content: 276535896.68354434
content: 60+276535896.68354434
next_content_add_sub: 60+276535896.68354434
['60', '276535896.68354434']
add_sub_content: 276535956.68354434
content: 276535956.68354434
next_expression: -1-2*(276535956.68354434-(-4*3)/(16-3*2))+56+(56-45)
content: -4*3
next_content_mul_div: 4*3
['4', '3']
mul_div_content: 12.0
content: -12.0
next_expression: -1-2*(276535956.68354434--12.0/(16-3*2))+56+(56-45)
content: 16-3*2
next_content_mul_div: 3*2
['3', '2']
mul_div_content: 6.0
content: 16-6.0
next_content_add_sub: 16-6.0
['16', '6.0']
add_sub_content: 10.0
content: 10.0
next_expression: -1-2*(276535956.68354434--12.0/10.0)+56+(56-45)
content: 276535956.68354434--12.0/10.0
next_content_mul_div: 12.0/10.0
['12.0', '10.0']
mul_div_content: 1.2
content: 276535956.68354434--1.2
next_content_add_sub: 276535956.68354434--1.2
['276535956.68354434', '', '1.2']
add_sub_content: 276535955.48354435
content: 276535955.48354435
next_expression: -1-2*276535955.48354435+56+(56-45)
content: 56-45
next_content_add_sub: 56-45
['56', '45']
add_sub_content: 11.0
content: 11.0
next_expression: -1-2*276535955.48354435+56+11.0
next_content_mul_div: 2*276535955.48354435
['2', '276535955.48354435']
mul_div_content: 553071910.9670887
content: -1-553071910.9670887+56+11.0
next_content_add_sub: -1-553071910.9670887
['', '1', '553071910.9670887']
add_sub_content: -553071911.9670887
content: -553071911.9670887+56+11.0
next_content_add_sub: -553071911.9670887+56
['-553071911.9670887', '56']
add_sub_content: -553071855.9670887
content: -553071855.9670887+11.0
next_content_add_sub: -553071855.9670887+11.0
['-553071855.9670887', '11.0']
add_sub_content: -553071844.9670887
content: -553071844.9670887
-553071844.9670887
相關文章
相關標籤/搜索