GBK編碼和UTF-8編碼互轉的大坑


  這幾天遇到一個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錕斤拷錕斤拷


編碼過程中錯誤診斷參考
)一個漢字對應一個問號
在通過ISO-8859-1從字符串獲取字節數組時,
由於一個Unicode轉換成一個byte,
當遇到不認識的Unicode時,轉換為0x3F,
這樣無論用哪種編碼構造時都會產生一個?亂碼。


)一個漢字對應兩個問號
在通過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涓 枃


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM