作为一名程序员,无论是前端开发还是后端开发,都可能和正则表达式打交道,有时候明明肉眼可见是符合正则的,为何总是过不去呢?下面先贴一个例子,最近遇到的坑,有兴趣可以先试一下复制到记事本和IDE工具内看下。
程序员在线找BUG
如下是一个手机号,我们需要通过正则校验手机号合法性,看起来是完全符合手机号规则的
?13666668888?
不使用复杂的的正则校验,就校验数字11位,测试复制和手动输入的结果:
public static void main(String[] args) { System.out.println(ReUtil.isMatch ("(1){1}[0-9]{10}", "\u202D13666668888\u202C")); System.out.println(ReUtil.isMatch ("(1){1}[0-9]{10}", "13666668888")); }
这是什么奇奇怪怪的东西,为什么复制到IDE工具内就多了些奇奇怪怪的,\u202D和\u202C显然是Unicode编码,具体不在做多解释,查阅Unicode编码范围可知:
2000-206F:常用标点(General Punctuation)
这是标点范围内的,又是不可见的标点,一般人很难察觉。
遇到这个问题的场景是:我方使用联系方式请求第三方接口,报联系方式格式错误,第一反应就是对方接口出问题了,截图和使用文本文档复制报文给对方(见下图),对方也认为是自身问题,因为报文看起来有问题,对方表示他们打出的日志也是这样,不存在编码格式不统一问题,直到对方说他手打报文发送就没问题,我才意识到,是不是有什么奇奇怪怪的字符在捉妖,毕竟以前遇到过带空格,带tab的情况。
问题报文
二话不说,报文复制到IDE看一眼,当初查换行和tab就是这样做的。
代码正如之前所发的,有两个Unicode编码,再次和录入联系方式的人员确认:
似乎马上要破案了,联系方式是对方微信发的,可能是从哪里复制过来的,那么,一般联系会从通讯录或者其他地方复制,网上一搜,果然水果机是有这种机制的,那么为何要引入这些Unicode编码呢?查到有如下说法
在所有主要的Web浏览器中内存中的字符顺序(逻辑)与它们显示的顺序(可视)是不同的。Unicode 定义了它其中每个字符的方向属性,浏览器应用的一组规则(通过这个来进行自动判断文本Unicode方向属性应该使用哪种方向)在显示时产生正确的顺序由Unicode双向算法进行描述,也可简称为BIDI算法。控制字符,有时候也称非打印字符,是出现在特定的信息文本中,表示某一控制功能的字符。这类字符并不显示,只包含某种特定的功能。
说人话,就是有些场景,数据库之类的存储的是左到右,但是显示不行,据说阿拉伯文相关的环境是右到左的,中文英文是左到右,那么要做到国际化就要对对应文本进行处理。
日常我们书写文字会知道,书写的方向是决定于所书写的文字,汉字、拉丁文字是从左至右,阿拉伯文、希伯来文则是从右至左。相应的,Unicode 字符在设计时就考虑了不同文字方向性的问题,因此定义了每个 Unicode 字符的方向属性。
只定义每个Unicode字符方向还是不足够的,很多时候需要将整体字符串左右反转,那么控制字符就派上用场了。
U+202A: LEFT-TO-RIGHT EMBEDDING (LRE)
U+202B: RIGHT-TO-LEFT EMBEDDING (RLE)
U+202D: LEFT-TO-RIGHT OVERRIDE (LRO)
U+202E: RIGHT-TO-LEFT OVERRIDE (RLO)
U+202C: POP DIRECTIONAL FORMATTING (PDF)
PDF即是终点,水果机使用LRO和PDF即达到了控制效果,我们可以测试下LRO相反的RLO的作用,从override的意思来看,就是覆盖了原有排版,上代码测试:
public static void main(String[] args) { System.out.println("13666668888"); System.out.println("\u202D13666668888\u202C"); System.out.println("\u202E13666668888\u202C"); }
13666668888
?13666668888?
?13666668888?
有兴趣的可以复制最后一个到IDE或者文本编辑工具看下效果。
水果机通过控制字符控制了字符的左右方向和排版方向,但是这可苦了我们这些程序员了,一不小心就找不到问题所在了,所以对于此类问题,最好就是前端或者后端做正则校验或者替换掉有问题的字符,指望系统操作人员去手打,既容易出错还不方便。相关示例代码如下:
public static void main(String[] args) { System.out.println("13666668888"); System.out.println("\u202D13666668888\u202C"); System.out.println("\u202E13666668888\u202C".replaceAll("\\p{Cf}","")); }
13666668888
?13666668888?
13666668888
可以看到第三个顺序展示了,其中\p{Cf}是不显示的Uniicode的一个总称,经测试邮箱内的特殊字符、区号或者分机号和电话相连的-是不会被替换的,不过查阅了下有\p{P}的说明,也有参数为C的说明,但是没Cf的说明,不知道哪位大神可以补充说明下。
以上就是史上最坑空白字符的发现过程和具体用途,有兴趣的可以就里面具体的点再查阅下相关文档,此处不再叙述。
- 最新留言
-
- 内容很有深度!http://v8lf.desaypower.com.cn/
- 楼上的别说的那么悲观好吧!http://282.desaypower.com.cn/
- 有钱、有房、有车,人人都想!http://1wo.zoneby-ep.cn/
- 看帖回帖一条路!http://vp0.zoneby-ep.cn/
- 刚看见一个妹子,很漂亮!http://jzgz4.zoneby-ep.cn/
- 支持一下,下面的保持队形!http://3bi.net/post/2705.html/
- 感谢楼主的推荐!http://3bi.net/post/2705.html/
- 我只看看不说话。。。http://3bi.net/post/2705.html/
- 祖国尚未统一,我却天天灌水,好内疚!http://3bi.net/post/2705.html/
- 楼主说的我也略懂!http://kp1.xmhmzx.com.cn/
- 文章归档
-
- 2025年5月 (103)
- 2025年4月 (371)
- 2025年3月 (377)
- 2025年2月 (338)
- 2025年1月 (389)
- 2024年12月 (374)
- 2024年11月 (361)
- 2024年10月 (374)
- 2024年9月 (360)
- 2024年8月 (381)
- 2024年7月 (388)
- 2024年6月 (370)
- 2024年5月 (372)
- 2024年4月 (363)
- 2024年3月 (375)
- 2024年2月 (355)
- 2024年1月 (394)
- 2023年12月 (376)
- 2023年11月 (366)
- 2023年10月 (711)
- 2023年9月 (2758)
- 2023年8月 (2837)
- 2023年7月 (2637)
- 2023年6月 (2433)
- 2023年5月 (3416)
- 2023年4月 (3248)
- 2023年3月 (3478)
- 2023年2月 (3105)
- 2023年1月 (3491)
- 2022年12月 (3470)
- 2022年11月 (2403)
- 2022年10月 (2253)
- 2022年9月 (1263)
- 2022年8月 (1298)
- 2022年7月 (1495)
- 2022年6月 (1954)
- 2022年5月 (2930)
- 2022年4月 (2604)
- 2022年3月 (1050)
- 2022年1月 (2774)
- 2021年12月 (2199)
- 2021年11月 (2905)
- 2021年10月 (1319)
- 2021年9月 (1822)
Powered By Z-BlogPHP,Theme By 冀ICP备2022013555号,Copyright Your WebSite.Some Rights Reserved.