java 对方 GBK 乱码_【转】Java编码与乱码---GBK与UTF-8之间的转换

【GBK转UTF-8】在很多论坛、网上经常有网友问“ 为什么我使用 new String(tmp.getBytes("ISO-8859-1"), "UTF-8") 或者 new String(tmp.getBytes("ISO-8859-1"), "GBK")可以得到正确的中文,但是使用 new String(tmp.getBytes("GBK"), "UTF-8") 却不能将GBK转换成UTF-8呢?”

参考前面的【Java基础专题】编码与乱码(03)----String的toCharArray()方法测试一文,我们就知道原因了。因为如果客户端使用GBK、UTF-8编码,编码后的字节经过ISO-8859-1传输,再用原来相同的编码方式进行解码,这个过程是“无损的转换”---- 因为原始和最终的编码方式相同。

但是如果客户端使用GBK编码,到了服务器端要转换成UTF-8,或者相反的过程。想一想,字节还是那些字节,但是编码的规则变了。原来GBK编码后的4个字节要用UTF-8的每个字符3个字节的规则编码,怎么能不乱码呢?

所以从现在开始,不要再犯这种错误了。new String(tmp.getBytes("GBK"), "UTF-8") 这个过程,JVM内部是不会帮你自动对字节进行扩展以适应UTF-8的编码的。正确的方法应该是根据UTF-8的编码规则进行字节的扩充,即手动从2个字节变成3个字节,然后再转换成十六进制的UTF-8编码。

在这个专题的第一篇文章【Java基础专题】编码与乱码(01)---编码基础开头,我们就已经介绍了这个规则:

①得到每个字符的2进制GBK编码

②将该16进制的GBK编码转换成2进制的字符串(2个字节)

③分别在字符串的首位插入110,在第9位插入10,在第17位插入10三个字符串,得到3个字节

④将这3个字节分别转换成16进制编码,得到最终的UTF-8编码。

00143553O-0.JPG

下面给出一个从网络上得到的Java转码方法,原文链接见:http://jspengxue.javaeye.com/blog/40781。下面的代码做了小小的修改

package example.encoding;

/** *//**

* The Class CharacterEncodeConverter.

*/

public class CharacterEncodeConverter {

/** *//**

* The main method.

*

* @param args the arguments

*/

public static void main(String[] args) {

try {

CharacterEncodeConverter convert = new CharacterEncodeConverter();

byte[] fullByte = convert.gbk2utf8("中文");

String fullStr = new String(fullByte, "UTF-8");

System.out.println("string from GBK to UTF-8 byte:  " + fullStr);

} catch (Exception e) {

e.printStackTrace();

}

}

/** *//**

* Gbk2utf8.

*

* @param chenese the chenese

*

* @return the byte[]

*/

public byte[] gbk2utf8(String chenese) {

// Step 1: 得到GBK编码下的字符数组,一个中文字符对应这里的一个c[i]

char c[] = chenese.toCharArray();

// Step 2: UTF-8使用3个字节存放一个中文字符,所以长度必须为字符的3倍

byte[] fullByte = new byte[3 * c.length];

// Step 3: 循环将字符的GBK编码转换成UTF-8编码

for (int i = 0; i < c.length; i++) {

// Step 3-1:将字符的ASCII编码转换成2进制值

int m = (int) c[i];

String word = Integer.toBinaryString(m);

System.out.println(word);

// Step 3-2:将2进制值补足16位(2个字节的长度)

StringBuffer sb = new StringBuffer();

int len = 16 - word.length();

for (int j = 0; j < len; j++) {

sb.append("0");

}

// Step 3-3:得到该字符最终的2进制GBK编码

// 形似:1000 0010 0111 1010

sb.append(word);

// Step 3-4:最关键的步骤,根据UTF-8的汉字编码规则,首字节

// 以1110开头,次字节以10开头,第3字节以10开头。在原始的2进制

// 字符串中插入标志位。最终的长度从16--->16+3+2+2=24。

sb.insert(0, "1110");

sb.insert(8, "10");

sb.insert(16, "10");

System.out.println(sb.toString());

// Step 3-5:将新的字符串进行分段截取,截为3个字节

String s1 = sb.substring(0, 8);

String s2 = sb.substring(8, 16);

String s3 = sb.substring(16);

// Step 3-6:最后的步骤,把代表3个字节的字符串按2进制的方式

// 进行转换,变成2进制的整数,再转换成16进制值

byte b0 = Integer.valueOf(s1, 2).byteValue();

byte b1 = Integer.valueOf(s2, 2).byteValue();

byte b2 = Integer.valueOf(s3, 2).byteValue();

// Step 3-7:把转换后的3个字节按顺序存放到字节数组的对应位置

byte[] bf = new byte[3];

bf[0] = b0;

bf[1] = b1;

bf[2] = b2;

fullByte[i * 3] = bf[0];

fullByte[i * 3 + 1] = bf[1];

fullByte[i * 3 + 2] = bf[2];

// Step 3-8:返回继续解析下一个中文字符

}

return fullByte;

}

}

最终的测试结果是正确的:string from GBK to UTF-8 byte:  中文。

但是这个方法并不是完美的!要知道这个规则只对中文起作用,如果传入的字符串中包含有单字节字符,如a+3中文,那么解析的结果就变成:string from GBK to UTF-8 byte:  ?????????中文了。为什么呢?道理很简单,这个方法对原本在UTF-8中应该用单字节表示的数字、英文字符、符号都变成3个字节了,所以这里有9个?,代表被转换后的a、+、3字符。

所以要让这个方法更加完美,最好的方法就是加入对字符Unicode区间的判断

UCS-2编码(16进制) UTF-8 字节流(二进制)

0000 - 007F 0xxxxxxx

0080 - 07FF 110xxxxx 10xxxxxx

0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx

汉字的Unicode编码范围为\u4E00-\u9FA5 \uF900-\uFA2D,如果不在这个范围内就不是汉字了。【UTF-8转GBK】道理和上面的相同,只是一个逆转的过程,不多说了

但是最终的建议还是:能够统一编码就统一编码吧!要知道编码的转换是相当的耗时的工作

原始地址:http://www.fengfly.com/plus/view-173809-1.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中进行UTF-8到GBK码,如果使用不当会导致码问题。一种错误的用法是将UTF-8格式的字节流直接使用GBK进行解码,这是不合规矩的。因为UTF-8和GBK是不同的字符集,它们的编码方式不同。就好比一个美国人说英语,让一个不懂英文又不会学舌的日本人听,然后传递消息给另一个美国人,这样会导致消息的失真。因此,在解决字符集码问题时,一般需要显示指定编码和解码字符集。 正确的UTF-8到GBK码操作应该是这样的: ``` byte[] utfByte = str.getBytes("UTF-8"); // 将字符串以UTF-8编码转换为字节流 String gbk = new String(utfByte, "GBK"); // 将UTF-8的字节流以GBK解码为字符串 ``` 这样可以确保字符集的正确转换,避免出现码问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [再谈java码:GBKUTF-8互尾部码问题分析](https://blog.csdn.net/weixin_34072637/article/details/91697486)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Java使用UTF-8或GBK编码后还是码问题的解决办法](https://blog.csdn.net/phker/article/details/131533428)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值