这几天遇到一个BUG,问题很简单,解决却花了3、4天,特意记录下来。
linux环境下,将默认编码设置为GBK以后,运行GBK编码的脚本,调用一个Java的jar包,然后总jar包中返回GBK字符串。但是不知道是哪里出了问题,返回的参数一直是问号乱码。
放上脚本代码:
#!/bin/bash #str="\"$1 $2 $3\"" str="\"http://iap.zh.gmcc.net/WebService/Notify.asmx chenliang3 短信测试\"" /flash/system/appr/SafeRun.bin 0 0 "/jre/bin/java -jar /appr/adiap.jar ${str}" 2>&1
放上调试时的Java代码:
1 import java.io.ByteArrayOutputStream; 2 import java.net.MalformedURLException; 3 4 import sun.misc.BASE64Decoder; 5 6 7 public class text { 8 9 public static void main(String[] args) throws MalformedURLException, Exception{ 10 11 //byte[] fullByte1 = new String(str.getBytes("ISO-8859-1"), "UTF-8").getBytes("GBK"); 12 //String fullStr = new String(fullByte1, "GBK"); 13 14 /* 假设环境是以GBK编码,将数据解码成GBK编码方式,但是任然是???乱码 15 * 可能一:数据在编码之前已经被编码成utf-8或者ISO-8859-1 16 * 可能二:在打包过程中,数据被重新编码 17 * String temp = new String(args[0].getBytes("GBK")); 18 * String temp1 = new String(args[0].getBytes("gb2312")); 19 */ 20 21 /* 测试是否打包影响编码,结果显示并非打包影响编码 22 * String a = new String("短信2测试"); 23 * String temp = new String(a.getBytes("GBK"),"utf-8"); 24 * String temp1 = new String(temp.getBytes("utf-8"),"GBK"); 25 * String ios = new String(a.getBytes("GBK"),"ISO-8859-1"); 26 * String ios2 = new String(ios.getBytes("ISO-8859-1"),"GBK"); 27 * 28 * System.out.print(a+‘\r‘); 29 * System.out.print(temp+‘\r‘); 30 * System.out.print(temp1+‘\r‘); 31 * System.out.print(ios+‘\r‘); 32 * System.out.print(ios2); 33 */ 34 35 /* 测试转为了ISO-8859-1还是UTF-8, 未能转回中文字符,应该转码成了UTF-8 36 * String ios2 = new String(args[0].getBytes("ISO-8859-1"),"GBK"); 37 */ 38 39 /*测试获取到字符串的准确编码,结果为UTF-8 40 * String whatsencode = getEncoding(args[0]); 41 * System.out.println(whatsencode); 42 */ 43 44 45 /* 是否能直接由UTF-8转为GBK,并未转回中文字符,任然为问好乱码 46 * String ios = new String(args[0].getBytes("UTF-8"),"GBK"); 47 * System.out.print(ios); 48 */ 49 50 /* 询问大学老师得知,main函数并不会对字符串编码进行变化, 51 * 那么会不会是脚本调用jar文件时会否进行编码转换 52 * 测试Windows下调用脚本是否会?乱码,脚本运行需要环境,测试不能,陷入困境 53 */ 54 55 /* 决定在shell脚本中将字符串转为base64编码以后传送过来,在java中解码完成后传送回脚本 56 * String a = new String("短信测试"); 57 * String txt64= getBASE64(a); 58 * System.out.println(txt64+‘\r‘); 59 */ 60 61 62 /* 63 String a = new String("短信测试"); 64 String txt64 = getEncoding(a); 65 System.out.println("-----------------"+‘\r‘); 66 System.out.println(txt64+‘\r‘); 67 String en = enUnicode(a); 68 System.out.println(en); 69 System.out.println(deUnicode(en)); 70 */ 71 System.out.println("-----------------"+‘\r‘); 72 System.out.println(enUnicode("tszQxbLiytQ= 短信测试")); 73 74 /*将接收到的16进制字符串数组转为字符串再转为字节数组,交换高低位*/ 75 76 StringBuffer stob = new StringBuffer(); 77 for(int i =0;i<args.length;i++){ 78 System.out.println(args[i]); 79 if(args[i].length() == 4){ 80 args[i] = swapHexHL(args[i]); 81 stob. append(args[i]); 82 } 83 } 84 String newStr = stob.toString(); 85 System.out.println(newStr); 86 String Upstr = newStr.toUpperCase(); 87 String deStr = deUnicode(Upstr); 88 System.out.println(deStr); 89 String utfStr = new String(deStr.getBytes("utf-8")); 90 System.out.println(utfStr); 91 92 93 //String newStr = "ccb6c5d0e2b2d4ca000a"; 94 //byte[] newBt = newStr.getBytes("GBK"); 95 //System.out.println(newBt); 96 97 //System.out.println(deUnicode("B6CCD0C5B2E2CAD40A00")); 98 /* 99 String txtde64 = getFromBASE64(args[0]); 100 System.out.println(txtde64); 101 */ 102 } 103 /*检测字符串编码*/ 104 public static String getEncoding(String str) { 105 String encode = "GB2312"; 106 try { 107 if (str.equals(new String(str.getBytes(encode), encode))) { 108 String s = encode; 109 return s; 110 } 111 } catch (Exception exception) { 112 } 113 encode = "ISO-8859-1"; 114 try { 115 if (str.equals(new String(str.getBytes(encode), encode))) { 116 String s1 = encode; 117 return s1; 118 } 119 } catch (Exception exception1) { 120 } 121 encode = "UTF-8"; 122 try { 123 if (str.equals(new String(str.getBytes(encode), encode))) { 124 String s2 = encode; 125 return s2; 126 } 127 } catch (Exception exception2) { 128 } 129 encode = "GBK"; 130 try { 131 if (str.equals(new String(str.getBytes(encode), encode))) { 132 String s3 = encode; 133 return s3; 134 } 135 } catch (Exception exception3) { 136 } 137 return ""; 138 } 139 /*对字符串进行Base64编码解码*/ 140 141 public static String getBASE64(String s) { 142 if (s == null) return null; 143 return (new sun.misc.BASE64Encoder()).encode( s.getBytes() ); 144 } 145 public static String getFromBASE64(String s) { 146 if (s == null) return null; 147 BASE64Decoder decoder = new BASE64Decoder(); 148 try { 149 byte[] b = decoder.decodeBuffer(s); 150 return new String(b); 151 } catch (Exception e) { 152 return null; 153 } 154 } 155 156 /*将中文与16进制转换*/ 157 private static String hexString = "0123456789ABCDEF"; 158 public static String enUnicode(String str) { 159 // 根据默认编码获取字节数组 160 byte[] bytes = str.getBytes(); 161 StringBuilder sb = new StringBuilder(bytes.length * 2); 162 // 将字节数组中每个字节拆解成2位16进制整数 163 for (int i = 0; i < bytes.length; i++) { 164 sb.append(hexString.charAt((bytes[i] & 0xf0) >> 4)); 165 sb.append(hexString.charAt((bytes[i] & 0x0f) >> 0)); 166 } 167 return sb.toString(); 168 } 169 public static String deUnicode(String bytes) { 170 ByteArrayOutputStream baos = new ByteArrayOutputStream( 171 bytes.length() / 2); 172 // 将每2位16进制整数组装成一个字节 173 for (int i = 0; i < bytes.length(); i += 2) 174 baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString 175 .indexOf(bytes.charAt(i + 1)))); 176 return new String(baos.toByteArray()); 177 } 178 179 /*对获得的16进制数据进行处理,高低位转换*/ 180 181 public static String swapHexHL(String temp){ 182 if (temp == null) return null; 183 String high = (String) temp.subSequence(0,2); 184 String low = (String) temp.subSequence(2,4); 185 String newString = low +high; 186 return newString; 187 } 188 /*去掉XML不认可的字符0x0-0x20*/ 189 public static String delcode(String in) { 190 StringBuffer out = new StringBuffer(); // Used to hold the output. 191 char current; // Used to reference the current character. 192 if (in == null || ("".equals(in))) 193 return ""; // vacancy test. 194 for (int i = 0; i < in.length(); i++) { 195 current = in.charAt(i); 196 if ((current == 0x9) || (current == 0xA) || (current == 0xD) 197 || ((current > 0x20) && (current <= 0xD7FF)) 198 || ((current >= 0xE000) && (current <= 0xFFFD)) 199 || ((current >= 0x10000) && (current <= 0x10FFFF)) 200 || (current < 0x0)) 201 out.append(current); 202 } 203 return out.toString().trim(); 204 } 205 }
放上从网上找来的乱码分析:
一个汉字对应两个问号 在通过GBK从字符串获取字节数组时,由于一个Unicode转换成两个byte,如果此时用ISO-8859-1或用UTF-8构造字符串就会出现两个问号。 若是通过ISO-8859-1构造可以再通过上面所说的错上加错恢复(即再通过从ISO-8859-1解析,用GBK构造); 若是通过UTF-8构造则会产生Unicode字符"\uFFFD",不能恢复,若再通过String-UTF-8〉ByteArray-GBK〉String,则会出现杂码,如a锟斤拷锟斤拷 编码过程中错误诊断参考 1)一个汉字对应一个问号 在通过ISO-8859-1从字符串获取字节数组时, 由于一个Unicode转换成一个byte, 当遇到不认识的Unicode时,转换为0x3F, 这样无论用哪种编码构造时都会产生一个?乱码。 2)一个汉字对应两个问号 在通过GBK从字符串获取字节数组时,由于一个Unicode转换成两个byte,如果此时用ISO-8859-1或用UTF-8构造字符串就会出现两个问号。 若是通过ISO-8859-1构造可以再通过上面所说的错上加错恢复(即再通过从ISO-8859-1解析,用GBK构造); 若是通过UTF-8构造则会产生Unicode字符"\uFFFD",不能恢复,若再通过String-UTF-8〉ByteArray-GBK〉String,则会出现杂码,如a锟斤拷锟斤拷 3)一个汉字对应三个问号在通过UTF-8从字符串获取字节数组时,由于一个Unicode转换成三个byte,如果此时用ISO-8859-1构造字符串就会出现三个问号;用GBK构造字符串就会出现杂码,如a涓 枃
最后还是没有解决乱码的问题,而是通过将字符串转16进制,在Java中转回的方式实现结果
放上最后的脚本代码:
1 #!/bin/bash 2 str1="\"$1 $2\"" //$1,$2,$3,是运行脚本时传送的参数 3 str2="$3" 4 str3=`echo ${str2} | od -h` 5 str4=`echo ${str3:8}` 6 /flash/system/appr/SafeRun.bin 0 0 "/jre/bin/java -jar /appr/adiap.jar ${str1} ${str4}" 2>&1
一个汉字对应两个问号
在通过GBK从字符串获取字节数组时,由于一个Unicode转换成两个byte,如果此时用ISO-8859-1或用UTF-8构造字符串就会出现两个问号。
若是通过ISO-8859-1构造可以再通过上面所说的错上加错恢复(即再通过从ISO-8859-1解析,用GBK构造);若是通过UTF-8构造则会产生Unicode字符"\uFFFD",不能恢复,若再通过String-UTF-8〉ByteArray-GBK〉String,则会出现杂码,如a锟斤拷锟斤拷
编码过程中错误诊断参考1)一个汉字对应一个问号在通过ISO-8859-1从字符串获取字节数组时,由于一个Unicode转换成一个byte,当遇到不认识的Unicode时,转换为0x3F,这样无论用哪种编码构造时都会产生一个?乱码。
2)一个汉字对应两个问号
在通过GBK从字符串获取字节数组时,由于一个Unicode转换成两个byte,如果此时用ISO-8859-1或用UTF-8构造字符串就会出现两个问号。
若是通过ISO-8859-1构造可以再通过上面所说的错上加错恢复(即再通过从ISO-8859-1解析,用GBK构造);若是通过UTF-8构造则会产生Unicode字符"\uFFFD",不能恢复,若再通过String-UTF-8〉ByteArray-GBK〉String,则会出现杂码,如a锟斤拷锟斤拷
3)一个汉字对应三个问号在通过UTF-8从字符串获取字节数组时,由于一个Unicode转换成三个byte,如果此时用ISO-8859-1构造字符串就会出现三个问号;用GBK构造字符串就会出现杂码,如a涓 枃