日常处理用户数据时,"识别文本中的手机号并对中间位打码" 是高频需求 ------ 但直接写正则可能踩坑(比如匹配到 11 位数字但不是手机号),文件读写也容易忽略编码和资源泄漏问题。
这篇文章带你从规则分析→正则优化→文件安全操作→异常处理,写一个更健壮的手机号脱敏工具。
一、先明确:手机号的 "合法规则" 是什么?
国内手机号不是 "任意 11 位数字",而是有明确号段限制:
- 开头为
1,第二位是3-9(覆盖 13/14/15/16/17/18/19 号段) - 总长度严格 11 位
- 不能是 "类似手机号的数字串"(比如快递单号里的 11 位数字)
二、第一步:写一个 "不踩坑" 的手机号正则
直接用1\d{10}会匹配到非手机号的 11 位数字,我们需要精准匹配号段:
python
# 精准匹配国内手机号(覆盖所有主流号段)
PHONE_PATTERN = re.compile(r'1[3-9]\d{9}')
1:固定开头[3-9]:限制第二位为 3-9(排除 10/11/12 开头的无效号段)\d{9}:后续 9 位数字(总长度 11 位)
三、第二步:安全的文件读写逻辑
直接用open()容易忘记关文件,或者遇到中文乱码 ------ 用with上下文管理器 + 指定编码是最佳实践:
python
def read_text_file(file_path: str) -> str:
"""安全读取文本文件,处理编码和文件不存在异常"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
raise ValueError(f"错误:文件{file_path}不存在")
except UnicodeDecodeError:
raise ValueError(f"错误:文件{file_path}编码不是UTF-8,请检查")
def write_text_file(file_path: str, content: list[str]) -> None:
"""安全写入文本文件,每行一个内容"""
with open(file_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(content))
四、第三步:实现 "提取 + 打码" 的核心逻辑
结合正则匹配和文件操作,完成从 "读文本" 到 "输出脱敏结果" 的全流程:
python
import re
from typing import List
class PhoneDesensitizer:
def __init__(self):
# 初始化手机号正则(预编译提升效率)
self.phone_pattern = re.compile(r'1[3-9]\d{9}')
def extract_phones(self, text: str) -> List[str]:
"""从文本中提取所有合法手机号"""
return self.phone_pattern.findall(text)
def mask_phones(self, phones: List[str]) -> List[str]:
"""对手机号中间4位打码(前3后4保留,中间替换为****)"""
return [f"{phone[:3]}****{phone[7:]}" for phone in phones]
def process_file(self, input_path: str, output_path: str) -> None:
"""完整流程:读文件→提取手机号→打码→写文件"""
# 1. 读取原始文本
raw_text = read_text_file(input_path)
# 2. 提取合法手机号
original_phones = self.extract_phones(raw_text)
if not original_phones:
print("未识别到任何手机号")
return
# 3. 打码处理
masked_phones = self.mask_phones(original_phones)
# 4. 写入结果文件
write_text_file(output_path, masked_phones)
print(f"处理完成!共脱敏{len(masked_phones)}个手机号,结果已保存到{output_path}")
五、完整使用示例
python
if __name__ == "__main__":
# 初始化脱敏工具
desensitizer = PhoneDesensitizer()
# 替换为你的实际文件路径
desensitizer.process_file(
input_path="用户数据.txt",
output_path="脱敏后_用户手机号.txt"
)
效果演示
-
原始文件
用户数据.txt:python客户1:13812345678(北京),客户2:19987654321(上海) 备注:订单号20231234567(注意:这是11位数字但不是手机号,不会被匹配) -
输出文件
脱敏后_用户手机号.txt:python138****5678 199****4321
六、进阶优化:避免匹配 "非手机号的 11 位数字"
如果文本中存在类似 "订单号" 的 11 位数字,可以通过前后非数字约束进一步精准匹配:
python
# 优化正则:要求手机号前后不是数字(避免匹配数字串中的11位片段)
PHONE_PATTERN = re.compile(r'(?<!\d)1[3-9]\d{9}(?!\d)')
(?<!\d):断言手机号前面不是数字(?!\d):断言手机号后面不是数字
总结
这个工具的核心亮点:
- 正则精准:覆盖所有合法号段,避免匹配无效数字;
- 文件安全 :处理编码和异常,用
with保证资源不泄漏; - 可复用:封装为类,方便嵌入其他项目