处理文本时最头疼的事就是 "精准提取"—— 从一大段文字里找出手机号、邮箱、网址。逐字搜索太笨,正则表达式(regex)才是正解:用一串 "模式" 描述你要匹配的文本,一次搞定查找、替换、提取、验证。
一、re 模块:五个核心函数
import re后,常用五个函数:
1.re.search(pattern, string):扫描整个字符串,返回第一个匹配位置,没有则返回 None。
2.re.match(pattern, string):只从开头匹配,开头不匹配就返回 None。
3.re.findall(pattern, string):返回所有非重叠匹配,返回列表(最常用)。
4.re.sub(pattern, repl, string, count=0):替换所有匹配,repl可以是字符串或函数,count限制替换次数。
5.re.split(pattern, string, maxsplit=0):按模式分割字符串。
二、元字符:正则的 "字母表"
表示字符:
表格
| 元字符 | 说明 |
|---|---|
. | 匹配除换行外任意单字符 |
\d/\D | 数字 / 非数字(等价[0-9]/[^0-9]) |
\w/\W | 单词字符(含下划线,Unicode 下含中文)/ 非单词字符 |
\s/\S | 空白字符 / 非空白字符 |
[xyz] | 匹配列出的任一字符 |
[^xyz] | 匹配未列出的任意字符 |
[a-z] | 字符范围 |
表示数量(跟在字符后面):
表格
| 元字符 | 说明 |
|---|---|
* | 零次或多次({0,}) |
+ | 一次或多次({1,}) |
? | 零次或一次({0,1}) |
{n} | 恰好 n 次 |
{n,} | 至少 n 次 |
{n,m} | n 到 m 次 |
?(非贪心) | 紧跟重复符后面,尽可能少匹配(默认贪心多匹配) |
表示边界:^匹配开头、$匹配结尾、\b单词边界、\B非单词边界。
匹配分组:x|y匹配 x 或 y;(pattern)捕获分组;(?P<name>pattern)命名分组;\num反向引用第 num 组。
三、原始字符串 r""
正则里满是反斜杠(\d、\w),强烈建议用原始字符串r"...",忽略转义,避免语法警告和踩坑:
import re text = "abcdef123456" print(re.search(r"\w+", text)) # 推荐 print(re.search("\w+", text)) # 会有 SyntaxWarning 警告四、实战一:匹配手机号
规则:以 1 开头,第二位是 3/4/5/7/8/9,后面 9 位数字:
import re test = ["13812345678", "11456817239", "19912345678", "17138412356", "1234567890", "14752345673", "1800123456"] pattern = r"^1[345789]\d{9}$" # 开头1 + 第二位数集 + 9位数字 + 结尾 for i in test: print(f"{i:20}{'合法' if re.match(pattern, i) else '非法'}")五、实战二:匹配邮箱
import re test = ["example@example.com", "user.name@subdomain.example.co", "username@.com", "@missingusername.com", "-dasd@qq.com"] pattern = r"[\w!#$%&'*+-/=?^`{|}~.]+@[\w!#$%&'*+-/=?^`{|}~.]+\.[a-zA-Z]{2,}$" for i in test: print(f"{i:40}{'合法' if re.match(pattern, i) else '非法'}")六、实战三:匹配 0~255 之间的数字
这个模式很经典,分段考虑:个位数 / 两位数、100~199、200~249、250~255:
import re test = ["0", "9", "50", "100", "199", "200", "255", "256", "-1", "01", "001"] pattern = r"^([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])$" for num in test: print(f"{num:5}{'合法' if re.match(pattern, num) else '非法'}")七、实战四:从 HTML 标签里抠出网址
用findall+ 分组把href="..."里的网址全取出来:
import re test = """<link rel="alternate" hreflang="zh" href="https://zh.wikipedia.org/wiki/x"> <link rel="alternate" hreflang="zh-Hans" href="https://zh.wikipedia.org/zh-hans/x">""" pattern = r'href="(.+?)"' # 非贪心匹配引号内的内容 for i in re.findall(pattern, test): print(i)八、实战五:替换数字为对应单词
re.sub的repl可以是函数,每次匹配调用一次:
import re test = "I have 2 apples and 3 oranges." num_map = {"1": "one", "2": "two", "3": "three", "4": "four", "5": "five"} print(re.sub(r"\d", lambda x: num_map[x.group(0)], test)) # I have two apples and three oranges.老哥收尾
正则一句话:
\d\w\s表示字符、*+?{n}表示数量、^$表示边界、()分组、r"..."原始串;查用 search、开头用 match、全提用 findall、替换用 sub、切分用 split。
下一篇是综合案例—— 把前面学的全用上,写一个客户信息管理系统。有问题随时回来找老哥。