中文数据网络传输转码与解码过程浅析

时间:2021-04-04 06:54:29

       网络中传输数据,尤其是中文必然会遇到,转码与解码过程,中文产生乱码问题也就发生在该过程的某一环节,下面我将用代码的方式模拟整个转码和解码过程,相信理解此文之后,对所有中文乱码都会找到原因并处理之。在此之前,我们首先解一下网络发送数据的过程。以中文为例:中文的传输过程具体可能是:内存中unicode -> 编码阶段gbk, gb18030,gb2312,utf8 -> 到ISO8859-1 ->最后到可能的base64编码。其实传输ISO8859-1的字符就已经可以进行转换了,后面要进行Base64编码,我个人理解是为了网络发送和接受数据串简单,仅仅用基本的64个字符表示而已(个人观点,如有纰漏请不吝赐教!)。本例为了方便理解中文转码过程,没有进行base64的再次编码,关于base64编码与解码比较简单,请不理解的求助于网络。

       下面我们开始上代码:

       代码: Byte2HexUtil.java

package zmx.util;

import java.math.BigInteger;
/**
*
* @author zhangwenchao
*
*/
public class Byte2HexUtil {

public static String bytes2hex01(byte[] bytes)
{
/**
* 第一个参数的解释,记得一定要设置为1
* signum of the number (-1 for negative, 0 for zero, 1 for positive).
*/
BigInteger bigInteger = new BigInteger(1, bytes);
return bigInteger.toString(16);
}


/**
* 方式二
*
* @param bytes
* @return
*/
public static String bytes2hex02(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
String tmp = null;
for (byte b : bytes)
{
// 将每个字节与0xFF进行与运算,然后转化为10进制,然后借助于Integer再转化为16进制
tmp = Integer.toHexString(0xFF & b);
if (tmp.length() == 1)// 每个字节8位,转为16进制标志,2个16进制位
{
tmp = "0" + tmp;
}
sb.append(tmp);
}

return sb.toString();

}

/**
* 方式三
*
* @param bytes
* @return
*/
public static String bytes2hex03(byte[] bytes)
{
final String HEX = "0123456789abcdef";
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes)
{
// 取出这个字节的高4位,然后与0x0f与运算,得到一个0-15之间的数据,通过HEX.charAt(0-15)即为16进制数
sb.append(HEX.charAt((b >> 4) & 0x0f));
// 取出这个字节的低位,与0x0f与运算,得到一个0-15之间的数据,通过HEX.charAt(0-15)即为16进制数
sb.append(HEX.charAt(b & 0x0f));
}

return sb.toString();
}

public static void main(String[] args) {
byte[] bytes = {10,23,24,54};
System.out.println(Byte2HexUtil.bytes2hex01(bytes));
System.out.println(Byte2HexUtil.bytes2hex02(bytes));
System.out.println(Byte2HexUtil.bytes2hex03(bytes));

}



}


         这是一个工具类主要用于将byte[]数组转换为16进制字符串,16进制也可以理解为2进制的表现形式。

2、转码与解码过程:

package zmx.test;

import zmx.util.Byte2HexUtil;



public class T10 {

public static void print(byte[] bytes) throws Exception{
for(byte b: bytes){
System.out.print(b+" "+ new String(new byte[]{b},"ISO8859-1")+" ");
}
System.out.println();
}

public static void print(String str) throws Exception{
for(int i=0;i<str.length();i++){
System.out.print(str.charAt(i)+" "+ ((byte)str.charAt(i))+" ");
}
System.out.println();
}

public static void main(String[] args) throws Exception {

String chinese = "abc中文"; //中文字符串

/*byte[] unicodes = chinese.getBytes("UNICODE");
System.out.println(unicodes.length);
print(unicodes);
System.out.println(Byte2HexUtil.bytes2hex03(unicodes));*/

byte[] bg2312 = chinese.getBytes("GB2312"); //根据某一中文编码(ASCall和ISO8859-1不包含中文)获取字节数组
System.out.println(bg2312.length); //不同的中文格式编码获取的字节数组长度不同。
print(bg2312);
String sender = new String(bg2312,"ISO8859-1");
System.out.println("发送的数据:"+sender); //将该字节数组根据ISO8859-1转换为网络可传输的形式
System.out.println(Byte2HexUtil.bytes2hex03(bg2312)); //本质上就是传输编码之后的字节数组

String receive = sender; //接受的数据
System.out.println("接收的数据:"+receive);
print(receive);
byte[] receiveBytes = sender.getBytes("ISO8859-1");

System.out.println(Byte2HexUtil.bytes2hex03(receiveBytes));

System.out.println(new String(receiveBytes,"GB2312"));
System.out.println(new String(receiveBytes,"GB2312").length());

}

}


测试结果:

7
97 a 98 b 99 c -42 Ö -48 Ð -50 Î -60 Ä
发送的数据:abcÖÐÎÄ
616263d6d0cec4
接收的数据:abcÖÐÎÄ
a 97 b 98 c 99 Ö -42 Ð -48 Î -50 Ä -60
616263d6d0cec4
abc中文
5


 

        通过代码我们可以很明显的看出,对于字符串:“abc中文”,在发送前我们可以将其转换为“unicode/gb2312/utf-8”等不同格式的字节码数组,例如:byte[] bg2312 = chinese.getBytes("GB2312");使用GB2312进行转码。将得到的字节数组准换成16进制字符串之后得到“616263d6d0cec4”,其中,英文占1个字节 中文占两个字节。根据gb2312的编码规范:61-a,62-b,63-c,d6d0-中,cece-文。使用其他编码格式转码也是同理。之后我们把该字节数组转换为网络中可以传输的ISO8859-1的字符串,使用String sender = new String(bg2312,"ISO8859-1");得到:abcÖÐÎÄ。其实给数据串就可以发送了。但是为了不产生特殊字符等,真正传输过程又做了base64变换。得到字符串之后在进行逆变换就可以恢复中文。

       现在是不是对编码解码有了更深的理解,其实本质上就是这个简单,乱码也就是在变换过程中产生的,望读者对乱码产生原因多加分析。