应使用预编译正则提取邮箱和手机号再脱敏:邮箱用\b[a-za-z0-9._%+-]+@[a-za-z0-9.-]+\.[a-za-z]{2,}\b,大陆手机号用1[3-9]\d{9}|(?:(?:\+86)?[-\s()]?1[3-9]\d{9}),脱敏时保留原长度与结构,避免破坏格式。

如何用正则匹配并替换邮箱和手机号,而不是简单 replace
直接用 strings.ReplaceAll 无法应对格式多变的敏感信息——比如邮箱可能带大小写、含中文域名(虽少见但需兼容),手机号可能有空格、横线、括号甚至+86前缀。必须用正则提取后再统一脱敏,否则漏匹配或误伤正常文本。
推荐两个预编译正则:
邮箱:
regexp.MustCompile(`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b`)<br>
手机号(大陆):<code>regexp.MustCompile(`1[3-9]\d{9}|(?:(?:\+86)?[-\s()]?1[3-9]\d{9})`)<p>注意:<br>
- 邮箱正则加了 <code>\b</code></p></code> 边界断言,避免匹配到 abc@example.com.xyz 中的 example.com 部分- 手机号正则支持常见分隔符,但不匹配纯数字11位以外的“伪号码”(如123456789012)
- 若需支持港澳台或国际号,得单独扩展分支,别硬塞进一个正则里
脱敏时保留原始长度和结构,避免破坏排版
把 user@example.com 直接替换成 ***@***.com 会压缩字符数,导致表格错位或 JSON 格式损坏。更稳妥的做法是按原长度生成掩码字符。
实操建议:
- 邮箱:保留用户名首尾各1字符 + @ + 域名首尾各1字符,中间用 * 填满,例如 u****r@e****e.com
- 手机号:固定格式为 138****1234(前3后4),但需确保替换后总长不变——若原文本是 +86 138-1234-5678,脱敏后应为 +86 138****5678,而非砍掉符号
关键点:
- 用 re.ReplaceAllStringFunc 提取所有匹配项,再逐个脱敏,而非 ReplaceAllString 直接替换
- 对每个匹配串,先记录其原始位置和长度,脱敏后用相同长度字符串填充(可用 strings.Repeat("*", len(orig)-7) + orig[len(orig)-4:] 这类逻辑)
大文本场景下性能不能只靠 regexp.ReplaceAllString
单次处理 10MB 文本时,如果每行都跑一遍全量正则,CPU 会明显卡顿。核心瓶颈不在匹配,而在反复创建子字符串和内存拷贝。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
优化手段:
- 用 re.FindAllIndex 获取所有匹配起止位置,再用 bytes.Buffer 拼接非敏感段 + 脱敏段,避免多次字符串拼接
- 对超长文本(>100MB),考虑分块处理:按换行符切分,每块独立脱敏,最后合并(注意跨块边界不会切开邮箱或手机号)
- 禁用 regexp.Compile 在循环内调用,所有正则必须提前 MustCompile 并复用
示例片段:
var buf bytes.Buffer
last := 0
for _, loc := range re.FindAllIndex([]byte(text), -1) {
buf.WriteString(text[last:loc[0]])
buf.WriteString(maskEmail(text[loc[0]:loc[1]]))
last = loc[1]
}
buf.WriteString(text[last:])
return buf.String()
测试时容易忽略的边界 case
线上出问题往往不是因为主干逻辑错,而是没覆盖这些场景:
- 邮箱中含 Unicode 字符(如 张三@example.cn):Go 的 regexp 默认不支持 Unicode 字母,需改用 \p{L} 类型,且开启 (?U) 标志
- 连续多个邮箱紧挨着(a@b.comc@d.org):普通正则会因重叠匹配失败,需设置 re.Longest()
- HTML 或 Markdown 中的链接(<a href="mailto:test@x.com"></a>):脱敏后可能破坏标签结构,建议先解析 DOM 或至少跳过引号内内容
- 日志中时间戳混入数字(2024-05-12 13812345678 INFO):手机号正则会误抓,可加负向断言 (? 和 <code>(?!\d)
真正难的不是写对正则,而是确认业务文本里到底有哪些“看起来像但其实不是”的干扰模式——得拿真实日志样本跑几轮,别只测 hello@world.com
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










