一段网上java常见escape和unescape方法的BUG

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源码:

/** * 实现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足以满足当前大部分场景。

时间: 2024-08-02 16:41:00

一段网上java常见escape和unescape方法的BUG的相关文章

javascript中escape()、unescape()、encodeURI()、encodeURIComponent()、decodeUR...

这些URI方法encodeURI.encodeURIComponent().decodeURI().decodeURIComponent()代替了BOM的escape()和unescape()方法.URI方法更可取,因为它们对所有Unicode符号编码,而BOM方法只能对ASCII符号正确编码.尽量避免使用escape()和unescape()方法.摘自 javascript advanced book. js对文字进行编码涉及3个函数:escape,encodeURI,encodeURICom

Java常见的异常大集合

算术异常类:ArithmeticExecption 空指针异常类:NullPointerException 类型强制转换异常:ClassCastException 数组负下标异常:NegativeArrayException 数组下标越界异常:ArrayIndexOutOfBoundsException 违背安全原则异常:SecturityException 文件已结束异常:EOFException 文件未找到异常:FileNotFoundException 字符串转换为数字异常:NumberF

【面试笔试】Java常见面试笔试总结

Java 基础 1.有哪些数据类型 Java定义了8种简单类型:byte.short.int.long.char.float.double和boolean. 2.面向对象的语言特征 封装.继承.多态 3.有几种訪问机制? 4.面向对象的多态性在JAVA中怎么实现的 方法的重写Overriding和重载Overloading是Java多态性的不同表现.重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现.假设在子类中定义某方法与其父类有相同

Java常见异常

在Java中提供了一些异常用来描述经常发生的错误,对于这些异常,有的需要程序员进行捕获处理或声明抛出,有的是由Java虚拟机自动进行捕获处理.Java中常见的异常类如下表: 异 常 类 说 明 ClassCastException 类型转换异常类 ClassNotFoundException 未找到相应的类 ArithmeticException 算术异常 ArrayIndexOutOfBoundsException 数组下标越界异常 ArrayStoreException 数组中包含不兼容的值

Java常见错误列表

Java常见错误列表: 找不到符号(symbol) 类X是public的,应该被声明在名为X.java的文件中 缺失类.接口或枚举类型 缺失X 缺失标识符 非法的表达式开头 类型不兼容 非法的方法声明;需要返回类型 数组越界(java.lang.ArrayIndexOutOfBoundsException) 字符越界(java.lang.StringIndexOutOfBoundsException) 类Y中的方法X参数不匹配 缺少return语句 精度损失 在解析时到达了文件结尾 执行不到的语

java常见异常集锦

1. java.lang.nullpointerexception 这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些操作中,比如图片未经初始化,或者图片创建时的路径错误等等.对数组操作中出现空指针,很多情况下是一些刚开始学习编程的朋友常犯的错误,即把数组的初始化和数组元素的初始化混淆起来了.数组的初始化是对数组分配需要的空间,而初始化后的数组,其中的元素并没有实例化,依然是空的,

Java 常见的异常错误分析大集合

算术异常类:ArithmeticExecption 空指针异常类:NullPointerException 类型强制转换异常:ClassCastException 数组负下标异常:NegativeArrayException 数组下标越界异常:ArrayIndexOutOfBoundsException 违背安全原则异常:SecturityException 文件已结束异常:EOFException 文件未找到异常:FileNotFoundException 字符串转换为数字异常:NumberF

java 常见的异常大集合

算术异常类:ArithmeticExecption 空指针异常类:NullPointerException 类型强制转换异常:ClassCastException 数组负下标异常:NegativeArrayException 数组下标越界异常:ArrayIndexOutOfBoundsException 违背安全原则异常:SecturityException 文件已结束异常:EOFException 文件未找到异常:FileNotFoundException 字符串转换为数字异常:NumberF

java Invalid escape sequence (valid ones are \b \t \n \f \r \&quot; \&#39; \\ )

java转义符我一直认为是很难理解的东西,我常常把\符号和/符号弄混,不知道那一个是转义符 . 在java语言中你要知道的\是一个讨厌一个人的物种,\总喜欢秀恩爱,一般都是成对出现的如\\,但是也会有少量的奇葩\不喜欢另一半\,他们喜欢其他的字符,这些奇葩的字符就和人类中的同性恋一样不和大多数人喜欢的一样,他们喜欢的是特别的,如\和b在一起表示字符的边界,\r在一起表示回车,\n表示换行 \t表示制表符,\f换页符号这些单独出现的\符号,都是特定的符号有特殊的含义,这些字符需要记住. 喜欢秀恩爱