Python——正则表达式语法与实践
摘要
在編寫處理字符串的程序或網頁時,經常會有查找符合某些復雜規則的字符串的需要,正則表達式就是用于描述這些規則的工具,換句話說正則表達式是一種工具,它定義了字符串的匹配模式(如何檢查一個字符串是否有跟某種模式匹配的部分或者從一個字符串中將與模式匹配的部分提取出來或者替換掉)。如果你在Windows操作系統中使用過文件查找并且在指定文件名時使用過通配符(\*和?),那么正則表達式也是與之類似的用來進行文本匹配的工具,只不過比起通配符正則表達式更強大,它能更精確地描述你的需求(當然你付出的代價是書寫一個正則表達式比打出一個通配符要復雜得多,要知道任何給你帶來好處的東西都是有代價的,就如同學習一門編程語言一樣),比如你可以編寫一個正則表達式,用來查找所有以0開頭,后面跟著2-3個數字,然后是一個連字號“-”,最后是7或8位數字的字符串(像028-12345678或0813-7654321),這不就是國內的座機號碼嗎。最初計算機是為了做數學運算而誕生的,處理的信息基本上都是數值,而今天我們在日常工作中處理的信息基本上都是文本數據,我們希望計算機能夠識別和處理符合某些模式的文本,正則表達式就顯得非常重要了。今天幾乎所有的編程語言都提供了對正則表達式操作的支持,Python通過標準庫中的re模塊來支持正則表達式操作。
我們可以考慮下面一個問題:我們從某個地方(可能是一個文本文件,也可能是網絡上的一則新聞)獲得了一個字符串,希望在字符串中找出手機號和座機號。當然我們可以設定手機號是11位的數字(注意并不是隨機的11位數字,因為你沒有見過“25012345678”這樣的手機號吧)而座機號跟上一段中描述的模式相同,如果不使用正則表達式要完成這個任務就會很麻煩。
關于正則表達式的相關知識,大家可以閱讀一篇非常有名的博客叫[《正則表達式30分鐘入門教程》](https://deerchao.net/tutorials/regex/regex.htm),讀完這篇文章后你就可以看懂下面的表格,這是我們對正則表達式中的一些基本符號進行的扼要總結。
正則表達式語法
| . | 匹配任意字符 | b.t | 可以匹配bat / but / b#t / b1t等 |
| \w | 匹配字母/數字/下劃線 | b\wt | 可以匹配bat / b1t / b_t等 但不能匹配b#t |
| \s | 匹配空白字符(包括\r、\n、\t等) | love\syou | 可以匹配love you |
| \d | 匹配數字 | \d\d | 可以匹配01 / 23 / 99等 |
| \b | 匹配單詞的邊界 | \bThe\b | |
| ^ | 匹配字符串的開始 | ^The | 可以匹配The開頭的字符串 |
| $ | 匹配字符串的結束 | .exe$ | 可以匹配.exe結尾的字符串 |
| \W | 匹配非字母/數字/下劃線 | b\Wt | 可以匹配b#t / b@t等 但不能匹配but / b1t / b_t等 |
| \S | 匹配非空白字符 | love\Syou | 可以匹配love#you等 但不能匹配love you |
| \D | 匹配非數字 | \d\D | 可以匹配9a / 3# / 0F等 |
| \B | 匹配非單詞邊界 | \Bio\B | |
| [] | 匹配來自字符集的任意單一字符 | [aeiou] | 可以匹配任一元音字母字符 |
| [^] | 匹配不在字符集中的任意單一字符 | [^aeiou] | 可以匹配任一非元音字母字符 |
| * | 匹配0次或多次 | \w* | |
| + | 匹配1次或多次 | \w+ | |
| ? | 匹配0次或1次 | \w? | |
| {N} | 匹配N次 | \w{3} | |
| {M,} | 匹配至少M次 | \w{3,} | |
| {M,N} | 匹配至少M次至多N次 | \w{3,6} | |
| \ | 分支 | foo\ | |
| (?#) | 注釋 | ||
| (exp) | 匹配exp并捕獲到自動命名的組中 | ||
| (?<name>exp) | 匹配exp并捕獲到名為name的組中 | ||
| (?:exp) | 匹配exp但是不捕獲匹配的文本 | ||
| (?=exp) | 匹配exp前面的位置 | \b\w+(?=ing) | 可以匹配I'm dancing中的danc |
| (?<=exp) | 匹配exp后面的位置 | (?<=\bdanc)\w+\b | 可以匹配I love dancing and reading中的第一個ing |
| (?!exp) | 匹配后面不是exp的位置 | ||
| (?<!exp) | 匹配前面不是exp的位置 | ||
| *? | 重復任意次,但盡可能少重復 | a.*b a.*?b | 將正則表達式應用于aabab,前者會匹配整個字符串aabab,后者會匹配aab和ab兩個字符串 |
| +? | 重復1次或多次,但盡可能少重復 | ||
| ?? | 重復0次或1次,但盡可能少重復 | ||
| {M,N}? | 重復M到N次,但盡可能少重復 | ||
| {M,}? | 重復M次以上,但盡可能少重復 |
Python對正則表達式的支持
Python提供了re模塊來支持正則表達式相關操作,下面是re模塊中的核心函數。
| compile(pattern, flags=0) | 編譯正則表達式返回正則表達式對象 |
| match(pattern, string, flags=0) | 用正則表達式匹配字符串 成功返回匹配對象 否則返回None |
| search(pattern, string, flags=0) | 搜索字符串中第一次出現正則表達式的模式 成功返回匹配對象 否則返回None |
| split(pattern, string, maxsplit=0, flags=0) | 用正則表達式指定的模式分隔符拆分字符串 返回列表 |
| sub(pattern, repl, string, count=0, flags=0) | 用指定的字符串替換原字符串中與正則表達式匹配的模式 可以用count指定替換的次數 |
| fullmatch(pattern, string, flags=0) | match函數的完全匹配(從字符串開頭到結尾)版本 |
| findall(pattern, string, flags=0) | 查找字符串所有與正則表達式匹配的模式 返回字符串的列表 |
| finditer(pattern, string, flags=0) | 查找字符串所有與正則表達式匹配的模式 返回一個迭代器 |
| purge() | 清除隱式編譯的正則表達式的緩存 |
| re.I / re.IGNORECASE | 忽略大小寫匹配標記 |
| re.M / re.MULTILINE | 多行匹配標記 |
說明:?上面提到的re模塊中的這些函數,實際開發中也可以用正則表達式對象的方法替代對這些函數的使用,如果一個正則表達式需要重復的使用,那么先通過compile函數編譯正則表達式并創建出正則表達式對象無疑是更為明智的選擇。
正則表達式實踐
例子1:驗證輸入用戶名和QQ號是否有效并給出對應的提示信息。
""" 驗證輸入用戶名和QQ號是否有效并給出對應的提示信息要求:用戶名必須由字母、數字或下劃線構成且長度在6~20個字符之間,QQ號是5~12的數字且首位不能為0 """ import redef main():username = input('請輸入用戶名: ')qq = input('請輸入QQ號: ')# match函數的第一個參數是正則表達式字符串或正則表達式對象# 第二個參數是要跟正則表達式做匹配的字符串對象m1 = re.match(r'^[0-9a-zA-Z_]{6,20}$', username)if not m1:print('請輸入有效的用戶名.')m2 = re.match(r'^[1-9]\d{4,11}$', qq)if not m2:print('請輸入有效的QQ號.')if m1 and m2:print('你輸入的信息是有效的!')if __name__ == '__main__':main()提示:?上面在書寫正則表達式時使用了“原始字符串”的寫法(在字符串前面加上了r),所謂“原始字符串”就是字符串中的每個字符都是它原始的意義,說得更直接一點就是字符串中沒有所謂的轉義字符啦。因為正則表達式中有很多元字符和需要進行轉義的地方,如果不使用原始字符串就需要將反斜杠寫作\\\\,例如表示數字的\\d得書寫成\\\\d,這樣不僅寫起來不方便,閱讀的時候也會很吃力。
例子2:從一段文字中提取出國內手機號碼。
下面這張圖是截止到2017年底,國內三家運營商推出的手機號段。
import redef main():# 創建正則表達式對象 使用了前瞻和回顧來保證手機號前后不應該出現數字pattern = re.compile(r'(?<=\D)1[34578]\d{9}(?=\D)')sentence = '''重要的事情說8130123456789遍,我的手機號是13512346789這個靚號,不是15600998765,也是110或119,王大錘的手機號才是15600998765。'''# 查找所有匹配并保存到一個列表中mylist = re.findall(pattern, sentence)print(mylist)print('--------華麗的分隔線--------')# 通過迭代器取出匹配對象并獲得匹配的內容for temp in pattern.finditer(sentence):print(temp.group())print('--------華麗的分隔線--------')# 通過search函數指定搜索位置找出所有匹配m = pattern.search(sentence)while m:print(m.group())m = pattern.search(sentence, m.end())if __name__ == '__main__':main()說明:?上面匹配國內手機號的正則表達式并不夠好,因為像14開頭的號碼只有145或147,而上面的正則表達式并沒有考慮這種情況,要匹配國內手機號,更好的正則表達式的寫法是:(?<=\D)(1[38]\d{9}|14[57]\d{8}|15[0-35-9]\d{8}|17[678]\d{8})(?=\D),國內最近好像有19和16開頭的手機號了,但是這個暫時不在我們考慮之列。
例子3:替換字符串中的不良內容
import redef main():sentence = '你丫是傻叉嗎? 我操你大爺的. Fuck you.'purified = re.sub('[操肏艸]|fuck|shit|傻[比屄逼叉缺吊屌]|煞筆','*', sentence, flags=re.IGNORECASE)print(purified) # 你丫是*嗎? 我*你大爺的. * you.if __name__ == '__main__':main()說明:?re模塊的正則表達式相關函數中都有一個flags參數,它代表了正則表達式的匹配標記,可以通過該標記來指定匹配時是否忽略大小寫、是否進行多行匹配、是否顯示調試信息等。如果需要為flags參數指定多個值,可以使用[按位或運算符](http://www.runoob.com/python/python-operators.html#ysf5)進行疊加,如`flags=re.I?|?re.M`。
例子4:拆分長字符串
import redef main():poem = '窗前明月光,疑是地上霜。舉頭望明月,低頭思故鄉。'sentence_list = re.split(r'[,。, .]', poem)while '' in sentence_list:sentence_list.remove('')print(sentence_list) # ['窗前明月光', '疑是地上霜', '舉頭望明月', '低頭思故鄉']if __name__ == '__main__':main()?python常用的表達式
1.郵箱
包含大小寫字母,下劃線,阿拉伯數字,點號,中劃線。
表達式:
[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)案例:?
pattern = re.compile(r"[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)")strs = '我的私人郵箱是zhuwjwh@outlook.com,公司郵箱是123456@qq.org,麻煩登記一下?' result = pattern.findall(strs)print(result) ['zhuwjwh@outlook.com', '123456@qq.org']2. 身份證號
xxxxxx yyyy MM dd 375 0 十八位
- 地區:[1-9]\d{5}
- 年的前兩位:(18|19|([23]\d)) 1800-2399
- 年的后兩位:\d{2}
- 月份:((0[1-9])|(10|11|12))
- 天數:(([0-2][1-9])|10|20|30|31) 閏年不能禁止29+
- 三位順序碼:\d{3}
- 兩位順序碼:\d{2}
- 校驗碼:[0-9Xx]
表達式:
[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]案例:
pattern = re.compile(r"[1-9]\d{5}(?:18|19|(?:[23]\d))\d{2}(?:(?:0[1-9])|(?:10|11|12))(?:(?:[0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]")strs = '小明的身份證號碼是342623198910235163,手機號是13987692110' result = pattern.findall(strs)print(result) ['342623198910235163']3. 國內手機號碼
手機號都為11位,且以1開頭,第二位一般為3、5、6、7、8、9 ,剩下八位任意數字 例如:13987692110、15610098778
表達式:
1(3|4|5|6|7|8|9)\d{9}案例:
pattern = re.compile(r"1[356789]\d{9}")strs = '小明的手機號是13987692110,你明天打給他' result = pattern.findall(strs)print(result) ['13987692110']4. 國內固定電話
區號3~4位,號碼7~8位
例如:0511-1234567、021-87654321
表達式:
\d{3}-\d{8}|\d{4}-\d{7}案例:
pattern = re.compile(r"\d{3}-\d{8}|\d{4}-\d{7}")strs = '0511-1234567是小明家的電話,他的辦公室電話是021-87654321' result = pattern.findall(strs)print(result) ['0511-1234567', '021-87654321']5. 域名
包含http:\\或https:\\
表達式:
(?:(?:http:\/\/)|(?:https:\/\/))?(?:[\w](?:[\w\-]{0,61}[\w])?\.)+[a-zA-Z]{2,6}(?:\/)案例:
pattern = re.compile(r"(?:(?:http:\/\/)|(?:https:\/\/))?(?:[\w](?:[\w\-]{0,61}[\w])?\.)+[a-zA-Z]{2,6}(?:\/)")strs = 'Python官網的網址是https://www.python.org/' result = pattern.findall(strs)print(result) ['https://www.python.org/']6. IP地址
IP地址的長度為32位(共有2^32個IP地址),分為4段,每段8位,用十進制數字表示 每段數字范圍為0~255,段與段之間用句點隔開
表達式:
((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))案例:
pattern = re.compile(r"((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))")strs = '''請輸入合法IP地址,非法IP地址和其他字符將被過濾! 增、刪、改IP地址后,請保存、關閉記事本! 192.168.8.84 192.168.8.85 192.168.8.86 0.0.0.1 256.1.1.1 192.256.256.256 192.255.255.255 aa.bb.cc.dd'''result = pattern.findall(strs)print(result) ['192.168.8.84', '192.168.8.85', '192.168.8.86', '0.0.0.1', '56.1.1.1', '192.255.255.255']7. 日期
常見日期格式:yyyyMMdd、yyyy-MM-dd、yyyy/MM/dd、yyyy.MM.dd
表達式:
\d{4}(?:-|\/|.)\d{1,2}(?:-|\/|.)\d{1,2}案例:
pattern = re.compile(r"\d{4}(?:-|\/|.)\d{1,2}(?:-|\/|.)\d{1,2}")strs = '今天是2020/12/20,去年的今天是2019.12.20,明年的今天是2021-12-20' result = pattern.findall(strs)print(result) ['2020/12/20', '2019.12.20', '2021-12-20']8. 國內郵政編碼
我國的郵政編碼采用四級六位數編碼結構 前兩位數字表示省(直轄市、自治區) 第三位數字表示郵區;第四位數字表示縣(市) 最后兩位數字表示投遞局(所)
表達式:
[1-9]\d{5}(?!\d)案例:
pattern = re.compile(r"[1-9]\d{5}(?!\d)")strs = '上海靜安區郵編是200040' result = pattern.findall(strs)print(result) ['200040']9. 密碼
密碼(以字母開頭,長度在6~18之間,只能包含字母、數字和下劃線)
表達式:
[a-zA-Z]\w{5,17}強密碼(以字母開頭,必須包含大小寫字母和數字的組合,不能使用特殊字符,長度在8-10之間)
表達式:
[a-zA-Z](?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10} pattern = re.compile(r"[a-zA-Z]\w{5,17}")strs = '密碼:q123456_abc' result = pattern.findall(strs)print(result) ['q123456_abc'] pattern = re.compile(r"[a-zA-Z](?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}")strs = '強密碼:q123456ABc,弱密碼:q123456abc' result = pattern.findall(strs)print(result) ['q123456ABc,']10. 中文字符
表達式:
[\u4e00-\u9fa5]案例:
pattern = re.compile(r"[\u4e00-\u9fa5]")strs = 'apple:蘋果' result = pattern.findall(strs)print(result) ['蘋', '果']11. 數字
驗證數字:^[0-9]*$ 驗證n位的數字:^\d{n}$ 驗證至少n位數字:^\d{n,}$ 驗證m-n位的數字:^\d{m,n}$ 驗證零和非零開頭的數字:^(0|[1-9][0-9]*)$ 驗證有兩位小數的正實數:^[0-9]+(.[0-9]{2})?$ 驗證有1-3位小數的正實數:^[0-9]+(.[0-9]{1,3})?$ 驗證非零的正整數:^\+?[1-9][0-9]*$ 驗證非零的負整數:^\-[1-9][0-9]*$ 驗證非負整數(正整數 + 0) ^\d+$ 驗證非正整數(負整數 + 0) ^((-\d+)|(0+))$ 整數:^-?\d+$ 非負浮點數(正浮點數 + 0):^\d+(\.\d+)?$ 正浮點數 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$ 非正浮點數(負浮點數 + 0) ^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 負浮點數 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$ 浮點數 ^(-?\d+)(\.\d+)?$12. 字符串
英文和數字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$ 長度為3-20的所有字符:^.{3,20}$ 由26個英文字母組成的字符串:^[A-Za-z]+$ 由26個大寫英文字母組成的字符串:^[A-Z]+$ 由26個小寫英文字母組成的字符串:^[a-z]+$ 由數字和26個英文字母組成的字符串:^[A-Za-z0-9]+$ 由數字、26個英文字母或者下劃線組成的字符串:^\w+$ 或 ^\w{3,20}$ 中文、英文、數字包括下劃線:^[\u4E00-\u9FA5A-Za-z0-9_]+$ 中文、英文、數字但不包括下劃線等符號:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$ 可以輸入含有^%&',;=?$\”等字符:`[^%&',;=?$\x22]+` 禁止輸入含有~的字符:[^~\x22]+博文參考
正則表達式測試工具:https://pythex.org/
re --- 正則表達式操作 — Python 3.10.0 文檔
https://github.com/2462612540/Machine-Learning/blob/2021/python_document/12.%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%92%8C%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F.md
使用Python驗證常見的50個正則表達式 - 云+社區 - 騰訊云
總結
以上是生活随笔為你收集整理的Python——正则表达式语法与实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爱奇艺视频cmd5x解析算法的移植分析和
- 下一篇: 扶我起来,Vue 3.0终于来了!