- 一、正則表達式
- 二、re模塊的基本使用
- 三、re模塊中常用功能函數
- 3.1 正則表達式的兩種書寫方式
- 3.2 re.compile(strPattern[, flag])函數
- 3.3 re.match(pattern, string[, flags])函數(常用)
- 3.4 分組函數
- 3.5 re.search(pattern, string[, flags])函數
- 3.6 re.findall(pattern, string[, flags])函數(常用)
- 3.7 re.split(pattern, string[, maxsplit])函數
- 3.8 re.sub(pattern, repl, string[, count])函數
- 3.9 re.subn(pattern, repl, string,[, count][, flags])函數
- 四、注意事項
- 五、計算器(經典)
一、正則表達式
正則表達式本身是一種小型的、高度專業化的編程語言,它並不是Python的一部分。正則表達式是用於處理字符串的強大工具,擁有自己獨特的語法以及一個獨立的處理引擎,效率上可能不如str自帶的方法,但功能十分強大。得益於這一點,在提供了正則表達式的語言里,正則表達式的語法都是一樣的,區別只在於不同的編程語言實現支持的語法數量不同;但不用擔心,不被支持的語法通常是不常用的部分。如果已經在其他語言里使用過正則表達式,只需要簡單看一看就可以上手了。而在python中,通過內嵌集成re模塊,程序員們可以直接調用來實現正則匹配。正則表達式模式被編譯成一系列的字節碼,然后由用C編寫的匹配引擎執行。
下圖展示了使用正則表達式進行匹配的流程:
正則表達式的大致匹配過程是:依次拿出表達式和文本中的字符比較,如果每一個字符都能匹配,則匹配成功;一旦有匹配不成功的字符則匹配失敗。如果表達式中有量詞或邊界,這個過程會稍微有一些不同,但也是很好理解的,看下圖中的示例以及自己多使用幾次就能明白。
下圖列出了Python支持的正則表達式元字符和語法:
1.1 數量詞的貪婪模式與非貪婪模式
正則表達式通常用於在文本中查找匹配的字符串。Python里數量詞默認是貪婪的(在少數語言里也可能是默認非貪婪),總是嘗試匹配盡可能多的字符;非貪婪的則相反,總是嘗試匹配盡可能少的字符。例如:正則表達式"ab"如果用於查找"abbbc",將找到"abbb"。而如果使用非貪婪的數量詞"ab?",將找到"a"。
1.2 反斜杠的困擾
與大多數編程語言相同,正則表達式里使用\
作為轉義字符,這就可能造成反斜杠困擾。假如你需要匹配文本中的字符\
,那么使用編程語言表示的正則表達式里將需要4個反斜杠\\\\
:前兩個和后兩個分別用於在編程語言里轉義成反斜杠,轉換成兩個反斜杠后再在正則表達式里轉義成一個反斜杠。Python里的原生字符串很好地解決了這個問題,這個例子中的正則表達式可以使用r'\\'
表示。同樣,匹配一個數字的\\d
可以寫成r'\d'
。有了原生字符串,你再也不用擔心是不是漏寫了反斜杠,寫出來的表達式也更直觀。
1.3 匹配模式
正則表達式提供了一些可用的匹配模式,比如忽略大小寫、多行匹配等,這部分內容將在Pattern類的工廠方法re.compile(pattern[, flags])中一起介紹。
二、re模塊的基本使用
正則表達式是用來匹配處理字符串的 python 中使用正則表達式需要引入re模塊
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)
['你', '好']
三、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
四、注意事項
- r原生字符:讓在python里有特殊意義的字符如\b,轉換成原生字符(就是去除它在python的特殊意義),不然會給正則表達式有沖突,為了避免這種沖突可以在規則前加原始字符r
- 正則表達式,返回類型為表達式對象的,如:<_sre.SRE_Match object; span=(6, 7), match='a'>,返回對象時,需要用正則方法取字符串,方法有:
- group() # 獲取匹配到的所有結果,不管有沒有分組將匹配到的全部拿出來,有參取匹配到的第幾個如2
- groups() # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分的結果
- groupdict() # 獲取模型中匹配到的分組結果,只拿出匹配到的字符串中分組部分定義了key的組結果
- 匹配到的字符串里出現空字符:注意:正則匹配到空字符的情況,如果規則里只有一個組,而組后面是*就表示組里的內容可以是0個或者多過,這樣組里就有了兩個意思,一個意思是匹配組里的內容,二個意思是匹配組里0內容(即是空白)所以盡量避免用*否則會有可能匹配出空字符串
- ()分組:注意:分組的意義,就是在匹配成功的字符串中,再提取()里的內容,也就是組里面的字符串
- ?:在有分組的情況下findall()函數,不只拿分組里的字符串,拿所有匹配到的字符串,注意?:只用於不是返回正則對象的函數如findall()
五、計算器(經典)
基於遞歸和正則將下面的字符串翻譯成計算器表達式,並且獲取最終結果: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 -*-
"""
該計算器思路:
1、遞歸尋找表達式中只含有 數字和運算符的表達式,並計算結果
2、由於整數計算會忽略小數,所有的數字都認為是浮點型操作,以此來保留小數
使用技術:
1、正則表達式
2、遞歸
"""
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