一段网上java常见escape和unescape方法的BUG
2015-08-17 10:25
435 查看
escape编码和unescape编码,就是将一个字符转换为16进制unicode编码,前面加%字符进行标识。
此处不再多做解释,参考这里:http://www.jb51.net/article/23657.htm。
原本是js的一个方法,后来被转成java方法。具体参考这里 http://blog.sina.com.cn/s/blog_4bb52a160100d9tm.html ,是被程序员们copy和paste最多的通用代码。
先看一下escape源码:
再看一下unescap方法:
代码逻辑很简单,分别解析了2宽度[0-255]和4宽度[4096-65535]的字符。
可是有2个问题:3宽度[256-4095]的字符存不存在?4字符以上的宽度存不存在?如果存在,这段代码就存在严重BUG,会导致解析失败。
先说第一个问题:
东亚语言及大部分语言unicode编码转换为16进制后都占4个宽度,但并不意味3宽度字符不存在。比如百度百科瑜伽的印第安语:योग,3个字符,转换16进制后分别占3个宽度。%u92f%u94b%u917,对此类字符上述代码就会unescape失败。
解决办法保证生成的>255的字符编码,有4个宽度。
红色注释4处的代码修改为:
或者
第二个问题:
十六进制4宽度代表2个字节。目前unicode的规范是ucs-2,即所有字符都以双字节存储。所以代码是可以搞定的。如果后续升级到ucs-4,甚至ucs-8,这段代码就肯定有问题了。不过,那应该是N年以后的事情了。ucs-2足以满足当前大部分场景。
此处不再多做解释,参考这里:http://www.jb51.net/article/23657.htm。
原本是js的一个方法,后来被转成java方法。具体参考这里 http://blog.sina.com.cn/s/blog_4bb52a160100d9tm.html ,是被程序员们copy和paste最多的通用代码。
先看一下escape源码:
/** * 实现js前台的escape()函数 * * @param src * @return */ public static String escape(String src) { int i; char j; StringBuffer tmp = new StringBuffer(); tmp.ensureCapacity(src.length() * 6); for (i = 0; i < src.length(); i++) { j = src.charAt(i);--字符转换为int值 if (Character.isDigit(j) || Character.isLowerCase(j) || Character.isUpperCase(j)) tmp.append(j);--1.如果是数字或者字母,直接使用 else if (j < 256) { tmp.append("%");--2.如果在[16-255],则加%前缀 if (j < 16) tmp.append("0");--3.如果字符编码<16,则前面加%0前缀,(补0以使编码2个字符宽度) tmp.append(Integer.toString(j, 16)); } else { tmp.append("%u"); tmp.append(Integer.toString(j, 16));--4.其他编码全部以%u为前缀
} } return tmp.toString(); }
再看一下unescap方法:
public static String unescape(String src) { StringBuffer tmp = new StringBuffer(); tmp.ensureCapacity(src.length()); int lastPos = 0, pos = 0; char ch; while (lastPos < src.length()) { pos = src.indexOf("%", lastPos);--查%号 if (pos == lastPos) { if (src.charAt(pos + 1) == 'u') { ch = (char) Integer.parseInt(src.substring(pos + 2, pos + 6), 16);//5--遇到%u,则读取后面的4个宽度字符,进行解码
tmp.append(ch); lastPos = pos + 6; } else { ch = (char) Integer.parseInt(src.substring(pos + 1, pos + 3), 16);//6--其他%,则读取2个宽度[0-255]的十六进展编码,进行解码 tmp.append(ch); lastPos = pos + 3; } } else { if (pos == -1) { tmp.append(src.substring(lastPos)); lastPos = src.length(); } else { tmp.append(src.substring(lastPos, pos)); lastPos = pos; } } } return tmp.toString(); }
代码逻辑很简单,分别解析了2宽度[0-255]和4宽度[4096-65535]的字符。
可是有2个问题:3宽度[256-4095]的字符存不存在?4字符以上的宽度存不存在?如果存在,这段代码就存在严重BUG,会导致解析失败。
先说第一个问题:
东亚语言及大部分语言unicode编码转换为16进制后都占4个宽度,但并不意味3宽度字符不存在。比如百度百科瑜伽的印第安语:योग,3个字符,转换16进制后分别占3个宽度。%u92f%u94b%u917,对此类字符上述代码就会unescape失败。
解决办法保证生成的>255的字符编码,有4个宽度。
红色注释4处的代码修改为:
if(j<4096){ tmp.append(0) } tmp.append(Integer.toString(j, 16));--4.其他编码全部以%u为前缀
或者
tmp.append(String.format("%04x",j))
第二个问题:
十六进制4宽度代表2个字节。目前unicode的规范是ucs-2,即所有字符都以双字节存储。所以代码是可以搞定的。如果后续升级到ucs-4,甚至ucs-8,这段代码就肯定有问题了。不过,那应该是N年以后的事情了。ucs-2足以满足当前大部分场景。
相关文章推荐
- java学习笔记-----qq项目----在服务器端实现两个客户端的通信的原理
- spring MVC 使用注解返回json
- Java数据类型的转换:隐式(自动)转换与强制转换
- 选择排序法--java实现
- Java compiler level does not match the version of the instal
- java代码读取properties文件
- Java删除文件夹和文件
- [Java]Contains Duplicate 包含重复数字
- javaweb学习总结——数据库连接池
- java中File的delete()方法删除文件失败的原因
- 30天了解30种技术系列---(11)Java开发者梦想的框架Play
- Java构造器的使用
- 关于@layout/~~布局时在java代码中失效的注意
- Java集合源码剖析(三)【TreeMap、LinkedHashmap】
- JAVA 设计模式 -- 适配器模式
- JDK5什么是新的线程锁技术(两)
- 如何实现简单的自动提示(autocomplete)填充搜索功能java代码。
- Java 成员变量与局部变量名重复
- JAVA学习笔记(十一):enum的使用
- 秦晓波著的编写高质量代码-改善Java程序的151个建议一书中的线程解释错误.