這幾天遇到一個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涓 枃