GBK编码和UTF-8编码互转的大坑

Posted blitheG

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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涓 枃

以上是关于GBK编码和UTF-8编码互转的大坑的主要内容,如果未能解决你的问题,请参考以下文章

Java工程编码格式由GBK转化成utf-8(编码格式互转)

QT utf-8与gbk编码互转code

utf-8和base64啥区别啊?

python编码类型互转总结

锟斤拷?UTF-8与GBK互转乱码问题

java怎么把UTF-8字串转成GBK字串?