需求
系统中有一列会用来存储 email 地址,现在需要对输入的字符串进行过滤,
要求是,把无效的地址过滤掉。有一些需要说明的是
- 这些地址是通过图像识别得到的,有些是用户自己输入的
- 已有历史记录已经存在了脏数据,需要替换
- 这个地址是识别出来的,不是用户帐号和联系信息这样的关键数据。所以宁愿相信用户是手误多录入的字符,或是机器识别把不该记录的字符当成 Email 的一部分了
测试字符串:
String[] arrEmailAddr = new String[]{
"uD83DuDC02abc123@cc.cc",
"A78=B[咔嚓]C⌚️2345@cc.cc",
"abcuD83CuDF32'sdfsd@sdfsd.·」cc",
"a·「d(*^@cc.cc",
"asl'''fgjk&^*'"234@sgd_slgkj-sdfsd.com"
};
方案和坑
本以为用了很多年正则,已经很熟练了,应该信手拈来,没想到实际操作时居然遇到那么多坑
首先可以确定的是,使用 Java 的 ReplaceAll
是没错了
-
网上广为流传的神 Pattern
// 清除掉所有特殊字符 String regEx="[`~!@#$%^&*()+=|{}':;',\[\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]"; Pattern p = Pattern.compile(regEx); Matcher m = p.matcher(str); return m.replaceAll("").trim();
首先,我是十分讨厌这种穷举法的;其次,对层出不穷的特殊字符控制不足,比如全角空格、颜文字等等。
pass
-
按照 Email 的格式严格匹配,把不符合 Email 格式的直接替换掉
是个比较彻底的方法,但是,实际操作前,发现有这样一些让人无语的脏数据:+--------+----------------------------------------------------+ | id | email | +--------+----------------------------------------------------+ | 66898 | 信箱三-mail:zhixxxxxxxxxx@163.com | | 115764 | 邮箱:1xxxxxx890@qq.com | | 513557 | M0:svxxxxxx@vip.163.c0m | | 708165 | 邮箱:lixxxxxxxx@zjlcwg.com | | 966373 | Mail:chenxxxxxx@ch-jht.com | +--------+----------------------------------------------------+ 5 rows in set (0.05 sec)
这些记录不在少数,肯定需要保留。
所以,问题还是回到 替换 这条思路上来 -
使用白名单
实际上只能这么做,难点和坑也在于这里,先列几个我犯的错误示例:String regEx = "/[0-9a-z]/"; // 这是一个正向测试,不过测试本身就有问题,Java里面不是用 “/” 来标识两端的 String regEx="[^0-9][^a-z][^_]"; // 这样会顺序匹配,在每个匹配组上干掉了其他组的合法字符 String regEx="(\W|@|-....)"; // 匹配到第一个之后,就直接过滤掉了,后面的其他字符不会被保留 String regEx="^[a-z0-9-_@]"; // ^ 字符匹配的是字符串开始的地方,无法作为 “非” 来使用,加转译就更不对了 String regEx="[^0-9a-z.-_@]"; // 这个就坑大了,自己看结果: /** ----- abc123@cc.cc A78=B[]C2345@cc.cc abcsdfsd@sdfsd.cc ad^@cc.cc aslfgjk^234@sgd_slgkjsdfsd.com ---- */ // 问题在于,“.” 这个字符,在里面会被认为是任意字符的匹配,对反斜线“免疫”
最终结果
测试字符
见文首
正则源码
正如上文说的,“.” 字符必须放末位
// 与下面的等价 String regEx="[^0-9a-z-_@.]";
String regEx="[^\w-@.]";
输出
abc123@cc.cc
782345@cc.cc
abcsdfsd@sdfsd.cc
ad@cc.cc
aslfgjk234@sgd_slgkj-sdfsd.com