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

时间:2023-01-05 10:18:59

  这几天遇到一个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涓 枃