网上开源的操作excel的包有很多,比较常用的是jexcelapi和apache的POI,jexcelapi就是以前提到过的jxl,说一下前不久使用jxl时出现的问题。
使用的jxl版本为4.6
由于在工程中使用jxl来处理一个excel文件,在读取中文字符时都出现了乱码,看了一下jxl的javadoc,先是设置了一下workbooksettings的编码,结果一样。然后试图通过转码找到
字符的原始编码,gb2312,utf-8等差不多四五种互相转换结果都不对。
没办法,只好下了jxl的源代码单步执行,最后跟到了这个方法。(jdk1.4.2)
public static String getUnicodeString(byte[] d, int length, int pos) { try { byte[] b = new byte[length * 2]; System.arraycopy(d, pos, b, 0, length * 2); return new String(b, "UnicodeLittle"); } catch (UnsupportedEncodingException e) { // Fail silently return ""; } } |
return以后就出现了乱码。
这句怎么看也找不出毛病,后来换用了jdk1.5竟然没有问题,中文字符被正确返回。
后来我将unicodelittle换成utf-16LE,之所以这样换,是因为我没见过有人这么写,
但如果没报这个UnsupportedEncodingException的话,应该是支持这种写法的,所以换得有点没有原因。更奇怪的是换了以后,结果正确了,在jdk1.4下。
于是怀疑jdk1.4 到1.5可能对这个unicodelittle作了修改,于是写了个JUNIT用unicodelittle返回一个中文字符的编码,换成16进制后,发现是1.4和1.5都一样。
(成了悬疑,希望有人能解答)
那究竟unicodelittle和utf-16LE有什么区别呢?原来用unicodelittle编码后字节数组中会带上BOM标志,而utf-16LE不会。
最终的解决办法是在源代码中找到jxl.biff.StringHelper这个类把
|
public static byte[] getUnicodeBytes(String s) { try { byte[] b = s.getBytes("UnicodeLittle");
// Sometimes this method writes out the unicode // identifier if (b.length == (s.length() * 2 + 2)) { byte[] b2 = new byte[b.length - 2]; System.arraycopy(b, 2, b2, 0, b2.length); b = b2; } return b; } catch (UnsupportedEncodingException e) { // Fail silently return null; } } |
和
public static String getUnicodeString(byte[] d, int length, int pos) { try { byte[] b = new byte[length * 2]; System.arraycopy(d, pos, b, 0, length * 2); return new String(b, "UnicodeLittle"); } catch (UnsupportedEncodingException e) { // Fail silently return ""; } } |
这个方法中的UnicodeLittle改成utf-16LE.
另外,把jxl.read.biff.BoundsheetRecord中的UnicodeLittle换成UTF-16LE
|
public BoundsheetRecord(Record t) { super(t); byte[] data = getRecord().getData(); offset = IntegerHelper.getInt(data[0], data[1], data[2], data[3]); typeFlag = data[5]; visibilityFlag = data[4]; length = data[6];
if (data[7] == 0) { // Standard ASCII encoding byte[] bytes = new byte[length]; System.arraycopy(data, 8, bytes, 0, length); name = new String(bytes); } else { // little endian Unicode encoding byte[] bytes = new byte[length * 2]; System.arraycopy(data, 8, bytes, 0, length * 2); try { name = new String(bytes, "UTF-16LE"); } catch (UnsupportedEncodingException e) { // fail silently name = "Error"; } } }
|
然后重新打包使用,一切OK。
最后说点题外话,自己找到解决办法以后上网搜了一下unicodelittle和JexcelAPI
竟然发现已经有人遇到这样的问题,并且也用相同的办法解决。于是感慨自己实在点背,花了一整天时间在这上面,早知道用这个搜一下就好了。
评论
想第一时间抢沙发么?