目錄
- 一、re 模塊的使用過程
- 二、正則表達式的字符匹配
- 1. 匹配開頭結尾
- 2. 匹配單個字符
- 3. 匹配多個字符
- 4. 匹配分組
- 5. Python 代碼示例
- 三、re 模塊的函數
- 1. 函數一覽表
- 2. Python 代碼示例
- 1)search 與 finditer
- 2)findall
- 3)sub
- 4)split
- 四、貪婪與非貪婪
- 五、re 模塊的可選標志
參考文章:
【re — 正則表達式操作】
【正則表達式指南】
【Python 正則表達式 | 菜鳥教程】
【Python 正則表達式 | 極客教程】
【Python正則表達式詳解 (超詳細,看完必會!)】
一、re 模塊的使用過程
# 導入 re 模塊
import re# 使用 match 方法進行匹配操作
# re.match() 能夠匹配出以 xxx 開頭的字符串
result = re.match(r'正則表達式', '被匹配的字符串')if result:# 如果上一步匹配到數據的話,可以使用 group 方法來提取數據print(result.group())
else:print('匹配失敗')
需要注意的是:
Python 中字符串前面加上 r 表示原生字符串。與大多數編程語言相同,正則表達式里使用 “\” 作為轉義字符,這就可能造成反斜杠困擾。Python 里的原生字符串很好地解決了這個問題,同時寫出來的表達式也更直觀。注意 r 只服務于 “\” ,不對其他進行轉義。
二、正則表達式的字符匹配
1. 匹配開頭結尾
字符 | 介紹 |
---|---|
^ | 匹配字符串的開頭 |
$ | 匹配字符串的末尾 |
2. 匹配單個字符
字符 | 介紹 |
---|---|
. | 匹配任意一個字符,除了換行符 \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_] |
\b | 匹配空字符串,但只在單詞開始或結尾的位置 |
\B | 匹配空字符串,但僅限于它不在單詞的開頭或結尾的情況 |
\A | 匹配字符串開始 |
\Z | 匹配字符串結尾 |
-
\ 是轉義特殊字符,\n 代表換行、\r 代表回車、\f 代表換頁、\t 代表 Tab 鍵
-
通過用 ‘-’ 將兩個字符連起來可以表示字符范圍,比如:
- [a-z] 將匹配任何小寫 ASCII 字符, [0-5][0-9] 將匹配從 00 到 59 的兩位數字,[0-9A-Fa-f] 將匹配任何十六進制數位。
- 如果 - 進行了轉義 (比如 [a\-z])或者它的位置在首位或末尾(如 [-a] 或 [a-]),那么它就只表示普通字符 ‘-’
-
除反斜杠外的特殊字符在 [ ] 中會失去其特殊含義,例如:[(+*)] 將匹配字符為 ( + * 或 ) 中的任何一個
3. 匹配多個字符
字符 | 介紹 |
---|---|
* | 匹配前一個字符出現 0 次或者無限次 |
+ | 匹配前一個字符出現 1 次或者無限次 |
? | 匹配前一個字符出現 1 次或者 0 次 |
{m} | 匹配前一個字符出現 m 次 |
{m,n} | 匹配前一個字符出現從 m 至 n 次 |
{m,} | 匹配前一個字符出現至少 m 次 |
-
ab* 會匹配 ‘a’,‘ab’,或者 ‘a’ 后面跟隨任意個 ‘b’
-
ab+ 會匹配 ‘a’ 后面跟隨 1 個以上到任意個 ‘b’,它不會匹配 ‘a’
-
ab? 會匹配 ‘a’ 或者 ‘ab’
-
a{6} 將匹配 6 個 ‘a’ , 少于 6 個的話就會導致匹配失敗
-
a{3,5} 將匹配 3 到 5 個 ‘a’
4. 匹配分組
字符 | 介紹 |
---|---|
| | 如果 A 和 B 是正則表達式,A|B 將匹配任何與 A 或 B 匹配的字符串 |
() | 將括號中的字符作為一個分組 |
\num | 引用分組 num 匹配到的字符串 |
(?P<name>) | 分組所匹配到了的字符串可通過符號分組名稱 name 來訪問 |
(?P=name) | 匹配前面叫 name 的命名組中所匹配到的串 |
5. Python 代碼示例
- 匹配出 126、163、qq 的郵箱地址,且 @ 符號之前有 4 到 20 位數字、大寫或小寫字母和下劃線的組合。
import reemail_list = ["Alice27@163.com", "Sam_1122@gmail.com", ".Tom_12@qq.com", "Bob_1988@qq.com.com", "Elowen@126.com", "Ice@qq.com", "nicetomeetyou@qq.com"]for email in email_list:ret = re.match(r"\w{4,20}@(163|126|qq)\.com$", email)if ret:print("%s 是符合規定的郵件地址 , 匹配后的結果是 : %s" % (email, ret.group()))else:print("%s 不符合要求" % email)
結果展示:
Alice27@163.com 是符合規定的郵件地址 , 匹配后的結果是 : Alice27@163.com
Sam_1122@gmail.com 不符合要求
.Tom_12@qq.com 不符合要求
Bob_1988@qq.com.com 不符合要求
Elowen@126.com 是符合規定的郵件地址 , 匹配后的結果是 : Elowen@126.com
Ice@qq.com 不符合要求
nicetomeetyou@qq.com 是符合規定的郵件地址 , 匹配后的結果是 : nicetomeetyou@qq.com
-
匹配 11 位不是以 4、7 結尾的手機號碼:
re.match(r"1\d{9}[0-35-68-9]$", tel)
-
提取區號和電話號碼:
ret = re.match(r"([^-]+)-(\d+)","010-12345678")
,此時 ret.group(1) = ‘010’ ,ret.group(2) = ‘12345678’ ,ret.group(0) 等價于 ret.group() 等于 ‘010-12345678’ -
匹配標簽,例如:匹配 <html><h1>hello</h1></html> 并提取出 hello
import retexts = ['<html><h1>hello</h1></html>', '<html><h1>world</h1></html1html>','<html><h1>hello</h2></html>']def match():for text in texts:ret = re.match(r'<([a-zA-Z]*)><([a-zA-Z0-9]*)>(\w*)</\2></\1>', text)if ret:print(f'匹配成功,標簽為 {ret.group()},信息為 {ret.group(3)}')else:print(f'{text} 標簽匹配失敗')if __name__ == '__main__':match()
結果展示:
匹配成功,標簽為 <html><h1>hello</h1></html>,信息為 hello
<html><h1>world</h1></html1html> 標簽匹配失敗
<html><h1>hello</h2></html> 標簽匹配失敗
- 用 (?P<name>) 和 (?P=name) 改進上述示例:
re.match(r'<(?P<name1>[a-zA-Z]*)><(?P<name2>[a-zA-Z0-9]*)>(\w*)</(?P=name2)></(?P=name1)>', text)
注意:字母 P 要大寫
三、re 模塊的函數
1. 函數一覽表
函數 | 含義 |
---|---|
re.compile(pattern, flags=0) | 將正則表達式的樣式編譯為一個正則表達式對象(正則對象),可以用于匹配 |
re.search(pattern, string, flags=0) | 掃描整個字符串,返回第一個匹配項 |
re.match(pattern, string, flags=0) | 從字符串開始位置匹配正則表達式 |
re.fullmatch(pattern, string, flags=0) | 如果整個 string 與正則表達式 pattern 匹配,則返回相應的 Match |
re.split(pattern, string, maxsplit=0, flags=0) | 按匹配項分割字符串 |
re.findall(pattern, string, flags=0) | 返回字符串中所有匹配的子串 |
re.finditer(pattern, string, flags=0) | 針對正則表達式 pattern 在 string 里的所有非重疊匹配返回一個產生 Match 對象的迭代器 |
re.sub(pattern, repl, string, count=0, flags=0) | 替換字符串中所有匹配正則表達式的部分 |
re.subn(pattern, repl, string, count=0, flags=0) | 行為與 sub() 相同,但是返回一個元組,包含 (替換后的字符串, 替換次數) |
re.escape(pattern) | 轉義 pattern 中的特殊字符,結果可直接用作匹配字符串 |
re.purge() | 清除正則表達式的緩存 |
-
result = re.match(pattern, string)
等價于prog = re.compile(pattern)
和result = prog.match(string)
。 -
re.split
函數中如果 maxsplit 非零,則最多進行 maxsplit 次分隔,剩下的字符全部返回到列表的最后一個元素。
2. Python 代碼示例
1)search 與 finditer
需求:匹配出文章閱讀的次數和點贊的次數。
import redef find_second_match(pattern, text):matches = re.finditer(pattern, text)try:next(matches) # 跳過第一個匹配項second_match = next(matches) # 獲取第二個匹配項return second_match.group()except StopIteration:return Noneif __name__ == '__main__':p = r"\d+"t = "閱讀次數為 9999 , 點贊次數為 19999"ret = re.search(p, t)print(f'閱讀次數為 {ret.group()} , 點贊次數為 {find_second_match(p, t)}')
結果展示:
閱讀次數為 9999 , 點贊次數為 19999
2)findall
- 需求:統計出相應文章閱讀、點贊和收藏的次數。
import reret = re.findall(r"\d+", "read = 9999, thumbs up = 7890, collection = 12345")
print(f'閱讀次數 {ret[0]} ,點贊次數 {ret[1]} ,收藏次數 {ret[2]}')
結果展示:
閱讀次數 9999 ,點贊次數 7890 ,收藏次數 12345
【拓展】:分組()
內加入 ?:
可以避免只返回分組內的內容。
- 需求:提取出字符串里的日期和時間 。
import reif __name__ == '__main__':s = 'hello world, now is 2020/7/20 18:48, 現在是 2020年7月20日18時48分。'ret_s = re.sub(r'年|月', r'/', s)ret_s = re.sub(r'日|分', r' ', ret_s)ret_s = re.sub(r'時', r':', ret_s)# hello world, now is 2020/7/20 18:48, 現在是 2020/7/20 18:48 。print(ret_s)# findallcom1 = re.compile(r'\d{4}/[01]?[0-9]/[1-3]?[0-9]\s(0[0-9]|1[0-9]|2[0-4])\:[0-5][0-9]')ret1 = com1.findall(ret_s)print(ret1[0]) # 18# 加 ?:com2 = re.compile(r'\d{4}/[01]?[0-9]/[1-3]?[0-9]\s(?:0[0-9]|1[0-9]|2[0-4])\:[0-5][0-9]')ret2 = com2.findall(ret_s)print(ret2[0]) # 2020/7/20 18:48# searchret3 = re.search(r'\d{4}/[01]?[0-9]/[1-3]?[0-9]\s(0[0-9]|1[0-9]|2[0-4])\:[0-5][0-9]', ret_s)print(ret3.group()) # 2020/7/20 18:48
3)sub
將匹配到的數據進行替換。
- 需求:將匹配到的閱讀次數加 1 。
import redef add(temp):str_num = temp.group()num = int(str_num) + 1return str(num)if __name__ == '__main__':ret = re.sub(r"\d+", '998', "thumbs up = 997")print(ret)ret = re.sub(r"\d+", add, "thumbs up = 997")print(ret)ret = re.sub(r"\d+", lambda x: str(int(x.group()) + 1), "thumbs up = 997")print(ret)print('-' * 30)# count 替換次數text = "apple apple apple apple"pattern = r"apple"replacement = "orange"new_text = re.sub(pattern, replacement, text, count=2)print(new_text)
結果展示:
thumbs up = 998
thumbs up = 998
thumbs up = 998
------------------------------
orange orange apple apple
- 需求:刪除字符串中所有的 HTML 標簽和
實體,只留下純文本內容。
import reif __name__ == '__main__':text = ('<div>' + '\n' +'<p>崗位職責:</p>' + '\n' +'<p>完成推薦算法、數據統計、接口、后臺等服務器端相關工作</p>' + '\n' +'<p><br></p>' + '\n' +'<p>必備要求:</p>' + '\n' +'<p>良好的自我驅動力和職業素養,工作積極主動、結果導向</p>' + '\n' +'<p> <br></p>' + '\n' +'<p>技術要求:</p>' + '\n' +'<p>1、一年以上 Python 開發經驗,掌握面向對象分析和設計,了解設計模式</p>' + '\n' +'<p>2、掌握 HTTP 協議,熟悉 MVC、MVVM 等概念以及相關 WEB 開發框架</p>' + '\n' +'<p>3、掌握關系數據庫開發設計,掌握 SQL,熟練使用 MySQL/PostgreSQL 中的一種<br></p>' + '\n' +'<p>4、掌握 NoSQL、MQ,熟練使用對應技術解決方案</p>' + '\n' +'<p>5、熟悉 Javascript/CSS/HTML5,JQuery、React、Vue.js</p>' + '\n' +'<p> <br></p>' + '\n' +'<p>加分項:</p>' + '\n' +'<p>大數據,數理統計,機器學習,sklearn,高性能,大并發。</p>' + '\n' +'</div>')# print(text)sub_result = re.sub(r"<[^>]*>| ", "", text)print(sub_result)
結果展示:
崗位職責:
完成推薦算法、數據統計、接口、后臺等服務器端相關工作必備要求:
良好的自我驅動力和職業素養,工作積極主動、結果導向技術要求:
1、一年以上 Python 開發經驗,掌握面向對象分析和設計,了解設計模式
2、掌握 HTTP 協議,熟悉 MVC、MVVM 等概念以及相關 WEB 開發框架
3、掌握關系數據庫開發設計,掌握 SQL,熟練使用 MySQL/PostgreSQL 中的一種
4、掌握 NoSQL、MQ,熟練使用對應技術解決方案
5、熟悉 Javascript/CSS/HTML5,JQuery、React、Vue.js加分項:
大數據,數理統計,機器學習,sklearn,高性能,大并發。
解析正則表達式:r"<[^>]*>| "
-
<[^>]*>
:匹配一對尖括號 < > 及其內部內容,且內容不包含 > 符號,表示匹配 HTML 標簽,比如 <div>, <a href=“…”> 等。 -
|
:表示 “或” 操作符。 -
:匹配 HTML 的不間斷空格實體
。 -
替換字符串是 “” ,即將匹配的內容替換為空字符串(刪除)。
4)split
根據匹配進行切割字符串,并返回一個列表。需求:切割字符串 “info:xiaoZhang 33 shandong” 。
import reret = re.split(r":| ","info:xiaoZhang 33 shandong")
print(ret)
結果展示:
['info', 'xiaoZhang', '33', 'shandong']
四、貪婪與非貪婪
Python 里數量詞默認是貪婪的,即總是嘗試匹配盡可能多的字符,而非貪婪則相反,總是嘗試匹配盡可能少的字符。
可以在 * , ? , + , {m,n} 后面加上非貪婪操作符 ? ,該操作符要求正則匹配的字符越少越好,因而可以將貪婪轉變為非貪婪。
Python 代碼示例:
import reif __name__ == '__main__':s = "This is a number 234-235-22-423"# 貪婪print(re.match(r".+(\d+-\d+-\d+-\d+)", s).group(1))print(re.match(r"aa(\d+)", "aa2343ddd").group(1))print(re.match(r"aa(\d+)ddd", "aa2343ddd").group(1))print('-' * 30)# 非貪婪print(re.match(r".+?(\d+-\d+-\d+-\d+)", s).group(1))print(re.match(r"aa(\d+?)", "aa2343ddd").group(1))print(re.match(r"aa(\d+?)ddd", "aa2343ddd").group(1))
結果展示:
4-235-22-423
2343
2343
------------------------------
234-235-22-423
2
2343
五、re 模塊的可選標志
標志 | 介紹 |
---|---|
re.I | 使匹配對大小寫不敏感 |
re.A | 不讓 \w 匹配漢字 |
re.L | 做本地化識別(locale-aware)匹配 |
re.M | 多行匹配,影響 ^ 和 $ |
re.S | 使 . 匹配包括換行符 \n 在內的所有字符 |
re.U | 根據 Unicode 字符集解析字符,這個標志影響 \w \W \b \B |
re.X | 該標志通過給予你更靈活的格式以便你將正則表達式寫得更易于理解 |
Python 代碼示例:
import reif __name__ == '__main__':s1 = 'hello\nworld'ret1 = re.match(r'hello.W', s1, re.S | re.I)if ret1:print(ret1.group()) # hello welse:print('no match')print('-' * 15)s2 = 'hello你好world'ret2 = re.match(r'hello\w*', s2, re.A)if ret2:print(ret2.group()) # helloelse:print('no match')