`

理解并解决GBK转UTF-8奇数中文乱码

    博客分类:
  • java
阅读更多

最近在做一个反馈功能,把数据反馈到对方公司网站,我公司是GBK编码,对方公司是UTF-8编码。因此,我需要将GBK编码数据转换成UTF-8编码数据,这样对方网站才不会乱码。最简单的方法是将HttpClient的ContentCharset设置为utf-8;如果ContentCharset是gbk并且又不想设置为utf-8,那么就需要将数据转换成UTF-8编码再发到对方网站。

 

问题出现:GBK转UTF-8时,奇数个中文会乱码,偶数个中文不会乱码。
三个中文

public static void encodeError() throws UnsupportedEncodingException {
	String gbk = "我来了";
	String utf8 = new String(gbk.getBytes("UTF-8"));

	//模拟UTF-8编码的网站显示
	System.out.println(new String(utf8.getBytes(),"UTF-8"));
}
/*
我来??
*/

 前面三个中文,后面一个中文,都是奇数

public static void encodeError2() throws UnsupportedEncodingException {
	String gbk = "今年是2011年";
	String utf8 = new String(gbk.getBytes("UTF-8"));

	//模拟UTF-8编码的网站显示
	System.out.println(new String(utf8.getBytes(),"UTF-8"));
}
/*
今年??011??
*/

 

原因:为什么只有奇数个中文才乱码,偶数个却不乱码?下面来分析原因

public static void analyze() throws UnsupportedEncodingException {
	String gbk = "我来了";
	String utf8 = new String(gbk.getBytes("UTF-8"));
	for (byte b : gbk.getBytes("UTF-8")) {
		System.out.print(b + " ");
	}
	System.out.println();
	for (byte b : utf8.getBytes()) {
		System.out.print(b + " ");
	}
}
/*
-26 -120 -111 -26 -99 -91 -28 -70 -122 
-26 -120 -111 -26 -99 -91 -28 -70 63 
*/

 注意最后一个字节不同,上面一行才是正确的UTF-8编码。那么为什么下面一行最后一个字节是63,而不是-122呢?这就是导致乱码的原因所在。
GBK编码是一个中文2个字节,而UTF-8编码是一个中文3个字节,当我们调用getBytes("UTF-8")方法时,会通过计算来增加字节,使得从GBK的2个字节变成UTF-8对应的3个字节。因此,上例3个中文输出了9个字节。

 

这里讲一下怎么通过计算增加字节,不深究的读者可以跳过此段。为了醒目,直接用代码讲解

public static void gbk2Utf() throws UnsupportedEncodingException {
	String gbk = "我来了";
	char[] c = gbk.toCharArray();
	byte[] fullByte = new byte[3*c.length];
	for (int i=0; i<c.length; i++) {
		String binary = Integer.toBinaryString(c[i]);
		StringBuffer sb = new StringBuffer();
		int len = 16 - binary.length();
		//前面补零
		for(int j=0; j<len; j++){
	    		sb.append("0");
	    	}
		sb.append(binary);
		//增加位,达到到24位3个字节
		sb.insert(0, "1110");
        	sb.insert(8, "10");
        	sb.insert(16, "10");
       		fullByte[i*3] = Integer.valueOf(sb.substring(0, 8), 2).byteValue();//二进制字符串创建整型
        	fullByte[i*3+1] = Integer.valueOf(sb.substring(8, 16), 2).byteValue();
        	fullByte[i*3+2] = Integer.valueOf(sb.substring(16, 24), 2).byteValue();
	}
	//模拟UTF-8编码的网站显示
	System.out.println(new String(fullByte,"UTF-8"));
}

 

现在我们来找出最后一个字节是63,而不是-122的原因。

public static void analyze2() throws UnsupportedEncodingException {
	String gbk = "我来了";
	byte[] utfBytes = gbk.getBytes("UTF-8");//得到9个字节
	String utf8 = new String(utfBytes);//问题就出在这
	System.out.print(utf8);
}
/*
鎴戞潵浜?
*/

 因为文件是GBK编码,new String(utfBytes)默认就是new String(utfBytes,"GBK")。它会2个字节2个字节地转换成字符,当字节是奇数时最后1个字节转字符就会计算错误,然后直接赋予最后这个字符为?,对应ASCII代码就是63。

 

解决问题
保证字节正确才是硬道理。当调用getBytes("UTF-8")转换成字节数组后,创建ISO-8859-1编码的字符串,ISO-8859-1编码是一个字节对应一个字符,因此不会使最后一个字节错误。

public static void correctEncode() throws UnsupportedEncodingException {
	String gbk = "我来了";
	String iso = new String(gbk.getBytes("UTF-8"),"ISO-8859-1");
	for (byte b : iso.getBytes("ISO-8859-1")) {
		System.out.print(b + " ");
	}
	System.out.println();

	//模拟UTF-8编码的网站显示
	System.out.println(new String(iso.getBytes("ISO-8859-1"),"UTF-8"));
}
/*
-26 -120 -111 -26 -99 -91 -28 -70 -122 
我来了
*/
 

 

分享到:
评论
40 楼 bruce.yuan 2016-05-23  
误人子弟的文章。已经看到N个人转了这个帖子,这要贻害多少新人啊。
lz先搞清楚各种编码的码表再说。解码编码说到底就是一个可逆的操作,如同序列化一样。不可逆了就会出现乱码,你要想准确解码你只要搞清楚它是哪种编码方式编码的就行了。
39 楼 lxn348567248 2012-12-17  
我感觉这样是不是有点麻烦,我们完全可以new String(byte,charset)这个方法去解决了。

38 楼 hobitton 2011-06-24  
iceman1952 写道
iceman1952 写道
beykery 写道
楼主有一点说的不正确,utf-8是变长编码,有1个字节到4个字节编码的字符。

是1个到6个

到底几个,到底几个,到底几个????

http://www.ietf.org/rfc/rfc3629.txt
37 楼 iceman1952 2011-06-22  
iceman1952 写道
beykery 写道
楼主有一点说的不正确,utf-8是变长编码,有1个字节到4个字节编码的字符。

是1个到6个

到底几个,到底几个,到底几个????
36 楼 iceman1952 2011-06-22  
beykery 写道
楼主有一点说的不正确,utf-8是变长编码,有1个字节到4个字节编码的字符。

是1个到6个
35 楼 330217445 2011-06-22  
别误导人了: 前提你说了,你必须确保你的文件是GBK编码!
	String str = new String("我来了".getBytes("GBK"));
		byte[] bt = str.getBytes("UTF-8");
		System.out.println(bt.length);
		for(int i=0;i<bt.length;i++){
			System.out.print(bt[i]+"  ");
		}
		String str1 = new String(bt,"UTF-8");

		
		System.out.println(new String(str1.getBytes(),"GBK"));



34 楼 jackra 2011-06-22  
<div class="quote_title">iceside 写道</div>
<div class="quote_div">..........
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span>字符串编码迷思:</span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr>
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span lang="EN-US">new String(input.getBytes("ISO-8859-1"), "GB18030")</span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span>上面这段代码代表什么?有人会说:</span><span lang="EN-US"> </span><span>“把</span><span lang="EN-US">input</span><span>字符串从</span><span lang="EN-US">ISO-8859-1</span><span>编码方式转换成</span><span lang="EN-US">GB18030</span><span>编码方式”。如果这种说法正确,那么又如何解释我们刚提到的</span><span lang="EN-US">java</span><span>字符串都采用</span><span lang="EN-US">unicode</span><span>编码呢?</span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span>这种说法不仅是欠妥的,而且是大错特错的,让我们一一来分析,其实事实是这样的:我们本应该用</span><span lang="EN-US">GB18030</span><span>的编码来读取数据并解码成字符串,但结果却采用了</span><span lang="EN-US">ISO-8859-1</span><span>的编码,导致生成一个错误的字符串。要恢复,就要先把字符串恢复成原始字节数组,然后通过正确的编码</span><span lang="EN-US">GB18030</span><span>再次解码成字符串(即把以</span><span lang="EN-US">GB18030</span><span>编码的数据转成</span><span lang="EN-US">unicode</span><span>的字符串)。注意,字符串永远都是</span><span lang="EN-US">unicode</span><span>编码的。</span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span>但编码转换并不是负负得正那么简单,这里我们之所以可以正确地转换回来,是因为</span><span lang="EN-US"> </span><span lang="EN-US">ISO8859-1</span><span lang="EN-US"> </span><span>是单字节编码,所以每个字节被按照原样</span><span lang="EN-US"> </span><span>转换为</span><span lang="EN-US"> </span><span lang="EN-US">String</span><span lang="EN-US"> </span><span>,也就是说,虽然这是一个错误的转换,但编码没有改变,所以我们仍然有机会把编码转换回来!</span><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span>总结:</span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span>所以,我们在处理</span><span lang="EN-US">java</span><span>的编码问题时,要分清楚三个概念:</span><span lang="EN-US">Java</span><span>采用的编码:</span><span lang="EN-US">unicode</span><span>,</span><span lang="EN-US">JVM</span><span>平台默认字符集和外部资源的编码。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
</div>
<p>这个地方是亮点</p>
33 楼 稍纵即逝 2011-06-22  
原来是要先转为ISO-8859-1,收下了
32 楼 pengmj 2011-06-21  
chenfeng0104 写道
arsenal04 写道
和刚看到的另两篇也是说编码的文章结合理解,有了一些收获,有个疑问,ISO-8859-1既然是单字节,那如果UTF-8转成字节数组占了3个字节,那是会转成3个字符吗?

是的,ISO-8859-1是一个字节表示一个字符,UTF-8是三个字节表示一个字符。

准确的说UTF-8表示中文时一般用3个字节
31 楼 codermouse 2011-06-21  
经常碰到中文乱码的问题,每次都是瞎猫碰死耗子一样的乱试。看了这个有点明白,留后面慢慢体会。
30 楼 I清晰 2011-06-21  
看不明白,这么多乱码
29 楼 chenfeng0104 2011-06-21  
chenfeng0104 写道
wisword 写道
chenfeng0104 写道
arsenal04 写道
和刚看到的另两篇也是说编码的文章结合理解,有了一些收获,有个疑问,ISO-8859-1既然是单字节,那如果UTF-8转成字节数组占了3个字节,那是会转成3个字符吗?

是的,ISO-8859-1是一个字节表示一个字符,UTF-8是三个字节表示一个字符。

这种说法不对,UTF-8有一个字节的字符也有2个字节的字符也有3个字节的字符

准确说是3个字节表示一个中文字符,英文字符或数字可以是1个字节,也可以是3个字节

28 楼 chenfeng0104 2011-06-21  
其实事实是这样的:我们本应该用
qdgyj 写道
holyselina 写道
String gbk = "我来了";  
String iso = new String(gbk.getBytes("UTF-8"),"UTF-8");
System.out.println(iso);
直接这样不行吗?

+1

String gbk = "我来了";
String iso = new String(gbk.getBytes("UTF-8"),"UTF-8");
for (byte b : gbk.getBytes("UTF-8")) {
        System.out.print(b + " ");
}
System.out.println();
for (byte b : iso.getBytes()) {
        System.out.print(b + " ");
}
System.out.println();
for (byte b : gbk.getBytes()) {
        System.out.print(b + " ");
}
/*
-26 -120 -111 -26 -99 -91 -28 -70 -122
-50 -46 -64 -76 -63 -53
-50 -46 -64 -76 -63 -53
*/
兄弟,自己体会
27 楼 chenfeng0104 2011-06-21  
for (byte b : iso.getBytes("ISO-8859-1")) {  
        System.out.print(b + " ");  
    }  
JavaStudyEye 写道
aiyust070 写道
twobowl_eye 写道
hobitton 写道
holyselina 写道
String gbk = "我来了";  
String iso = new String(gbk.getBytes("UTF-8"),"UTF-8");
System.out.println(iso);
直接这样不行吗?


lz那样转换纯粹是脱了裤子放屁,直接new String(s.getBytes("UTF-8"), "UTF-8")就搞定了非要中间加一层iso-8859-1的转换。
先utf8“编码”成byte数组,然后再utf8“解码”成字符串就可以了。

的确lz没有说到点子上,远程通信最终都要转成字节流的,关键是你传给outputstream的时候把String转成什么编码的字节数组,前面任你怎么转都是没有意义的

+1 这个是对的

确实这是对的,已测试


String gbk = "我来了";
String iso = new String(gbk.getBytes("UTF-8"),"UTF-8");
for (byte b : gbk.getBytes("UTF-8")) {
        System.out.print(b + " ");
}
System.out.println();
for (byte b : iso.getBytes()) {
        System.out.print(b + " ");
}
/*
-26 -120 -111 -26 -99 -91 -28 -70 -122
-50 -46 -64 -76 -63 -53
*/
26 楼 chenfeng0104 2011-06-21  
JavaStudyEye 写道
这样我就有一个疑问啊:小白问题,见笑

楼主提出的解决方案中有这样一句话:

“int len = 16 - binary.length();”

为啥用16作为减数呢

如果binary.length()是15,就在前面加个0,达到16位二进制
25 楼 chenfeng0104 2011-06-21  
wisword 写道
chenfeng0104 写道
arsenal04 写道
和刚看到的另两篇也是说编码的文章结合理解,有了一些收获,有个疑问,ISO-8859-1既然是单字节,那如果UTF-8转成字节数组占了3个字节,那是会转成3个字符吗?

是的,ISO-8859-1是一个字节表示一个字符,UTF-8是三个字节表示一个字符。

这种说法不对,UTF-8有一个字节的字符也有2个字节的字符也有3个字节的字符

准确说是3个字节表示一个中文字符
24 楼 JavaStudyEye 2011-06-21  
这样我就有一个疑问啊:小白问题,见笑

楼主提出的解决方案中有这样一句话:

“int len = 16 - binary.length();”

为啥用16作为减数呢
23 楼 JavaStudyEye 2011-06-21  
aiyust070 写道
twobowl_eye 写道
hobitton 写道
holyselina 写道
String gbk = "我来了";  
String iso = new String(gbk.getBytes("UTF-8"),"UTF-8");
System.out.println(iso);
直接这样不行吗?


lz那样转换纯粹是脱了裤子放屁,直接new String(s.getBytes("UTF-8"), "UTF-8")就搞定了非要中间加一层iso-8859-1的转换。
先utf8“编码”成byte数组,然后再utf8“解码”成字符串就可以了。

的确lz没有说到点子上,远程通信最终都要转成字节流的,关键是你传给outputstream的时候把String转成什么编码的字节数组,前面任你怎么转都是没有意义的

+1 这个是对的

确实这是对的,已测试
22 楼 qdgyj 2011-06-21  
holyselina 写道
String gbk = "我来了";  
String iso = new String(gbk.getBytes("UTF-8"),"UTF-8");
System.out.println(iso);
直接这样不行吗?

+1
21 楼 iceside 2011-06-21  
<p>
</p>
<p class="MsoNormal" style="margin-top: 22.5pt; margin-right: 0cm; margin-bottom: 15.0pt; margin-left: 0cm; text-align: left; line-height: 18.0pt;" align="left">下面这篇文章应该能够说到要点上。</p>
<p class="MsoNormal" style="margin-top: 22.5pt; margin-right: 0cm; margin-bottom: 15.0pt; margin-left: 0cm; text-align: left; line-height: 18.0pt;" align="left"><strong><span style="" lang="EN-US">Java</span></strong><strong><span style="">与<span lang="EN-US">Unicode</span></span></strong><strong><span style="" lang="EN-US"></span></strong></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">Java</span><span style="">的</span><span style="" lang="EN-US">class</span><span style="">文件采用</span><span style="" lang="EN-US">utf8</span><span style="">的编码方式,</span><span style="" lang="EN-US">JVM</span><span style="">运行时采用</span><span style="" lang="EN-US">utf16</span><span style="">。</span><span style="" lang="EN-US">Java</span><span style="">的字符串是</span><span style="" lang="EN-US">unicode</span><span style="">编码的。总之,</span><span style="" lang="EN-US">Java</span><span style="">采用了</span><span style="" lang="EN-US">unicode</span><span style="">字符集,使之易于国际化。</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US"> </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">Java</span><span style="">支持哪些字符集:</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">即</span><span style="" lang="EN-US">Java</span><span style="">能识别哪些字符集并对它进行正确地处理?查看</span><span style="" lang="EN-US">Charset</span><span style="" lang="EN-US"> </span><span style="">类,最新的</span><span style="" lang="EN-US">JDK</span><span style="">支持</span><span style="" lang="EN-US">160</span><span style="">种字符集。可以通过</span><span style="" lang="EN-US">static</span><span style="">方法</span><span style="" lang="EN-US">availableCharsets</span><span style="">拿到所有</span><span style="" lang="EN-US">Java</span><span style="">支持的字符集。</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">assertEquals(160, Charset.availableCharsets().size());  </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US"> </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">Set&lt;String&gt; charsetNames =
  Charset.availableCharsets().keySet();  </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US"> </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">assertTrue(charsetNames.contains("utf-8"));  </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">assertTrue(charsetNames.contains("utf-16"));  </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">assertTrue(charsetNames.contains("gb2312"));  </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US"> </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">assertTrue(Charset.isSupported("utf-8"));</span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US"> </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="">需要在哪些时候注意编码问题?</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="" align="left"><span style="" lang="EN-US">1.</span><span style="" lang="EN-US">        </span><span style="" lang="EN-US"> </span><span style="">从外部资源读取数据:</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">这跟外部资源采取的编码方式有关,我们需要使用外部资源采用的字符集来读取外部数据:</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">InputStream is = new
  FileInputStream("res/input2.data");  </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">InputStreamReader streamReader = new InputStreamReader(is,
  "GB18030");</span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">这里可以看到,我们采用了</span><span style="" lang="EN-US">GB18030</span><span style="">编码读取外部数据,通过查看</span><span style="" lang="EN-US">streamReader</span><span style="">的</span><span style="" lang="EN-US">encoding</span><span style="">可以印证:</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">assertEquals("GB18030", streamReader.getEncoding());</span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">正是由于上面我们为外部资源指定了正确的编码,当它转成</span><span style="" lang="EN-US">char</span><span style="">数组时才能正确地进行解码(</span><span style="" lang="EN-US">GB18030 -&gt; unicode</span><span style="">):</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">char[] chars = new char[is.available()];  </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">streamReader.read(chars, 0, is.available());</span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">但我们经常写的代码就像下面这样:</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">InputStream is = new
  FileInputStream("res/input2.data");  </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">InputStreamReader streamReader = new InputStreamReader(is);</span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">这时候</span><span style="" lang="EN-US">InputStreamReader</span><span style="">采用什么编码方式读取外部资源呢?</span><span style="" lang="EN-US">Unicode</span><span style="">?不是,这时候采用的编码方式是</span><span style="" lang="EN-US">JVM</span><span style="">的默认字符集,这个默认字符集在虚拟机启动时决定,通常根据语言环境和底层操作系统的</span><span style="" lang="EN-US"> </span><span style="" lang="EN-US">charset</span><span style="">来确定。可以通过以下方式得到</span><span style="" lang="EN-US">JVM</span><span style="">的默认字符集:</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">Charset.defaultCharset(); </span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">为什么要这样?因为我们从外部资源读取数据,而外部资源的编码方式通常跟操作系统所使用的字符集一样,所以采用这种默认方式是可以理解的。</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">好吧,那么我通过我的</span><span style="" lang="EN-US">IDE Ideas</span><span style="">创建了一个文件,并以</span><span style="" lang="EN-US">JVM</span><span style="">默认的编码方式从这个文件读取数据,但读出来的数据竟然是乱码。为何?呵呵,其实是因为通过</span><span style="" lang="EN-US">Ideas</span><span style="">创建的文件是以</span><span style="" lang="EN-US">utf-8</span><span style="">编码的。要得到一个</span><span style="" lang="EN-US">JVM</span><span style="">默认编码的文件,通过手工创建一个</span><span style="" lang="EN-US">txt</span><span style="">文件试试吧。</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="" align="left"><span style="" lang="EN-US">2.</span><span style="" lang="EN-US">        </span><span style="" lang="EN-US"> </span><span style="">字符串和字节数组的相互转换</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">我们通常通过以下代码把字符串转换成字节数组:</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">"string".getBytes();</span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">但你是否注意过这个转换采用的编码呢?其实上面这句代码跟下面这句是等价的:</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">"string".getBytes(Charset.defaultCharset());</span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">也就是说它根据</span><span style="" lang="EN-US">JVM</span><span style="">的默认编码(而不是你可能以为的</span><span style="" lang="EN-US">unicode</span><span style="">)把字符串转换成一个字节数组。</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">反之,如何从字节数组创建一个字符串呢?</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">new String("string".getBytes());</span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">同样,这个方法使用平台的默认字符集解码字节的指定数组(这里的解码指从一种字符集到</span><span style="" lang="EN-US">unicode</span><span style="">)。</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US"> </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="">字符串编码迷思:</span><span style="" lang="EN-US"></span></p>
<table class="MsoNormalTable" style="width: 100.0%; border-collapse: collapse;" border="0" cellspacing="0" cellpadding="0" width="100%"><tbody><tr style="">
<td style="width: 100.0%; background: silver; padding: 0cm 5.4pt 0cm 5.4pt;" width="100%" valign="top">
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US">new String(input.getBytes("ISO-8859-1"), "GB18030")</span><span style="" lang="EN-US"></span></p>
</td>
</tr></tbody></table>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">上面这段代码代表什么?有人会说:</span><span style="" lang="EN-US"> </span><span style="">“把</span><span style="" lang="EN-US">input</span><span style="">字符串从</span><span style="" lang="EN-US">ISO-8859-1</span><span style="">编码方式转换成</span><span style="" lang="EN-US">GB18030</span><span style="">编码方式”。如果这种说法正确,那么又如何解释我们刚提到的</span><span style="" lang="EN-US">java</span><span style="">字符串都采用</span><span style="" lang="EN-US">unicode</span><span style="">编码呢?</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">这种说法不仅是欠妥的,而且是大错特错的,让我们一一来分析,其实事实是这样的:我们本应该用</span><span style="" lang="EN-US">GB18030</span><span style="">的编码来读取数据并解码成字符串,但结果却采用了</span><span style="" lang="EN-US">ISO-8859-1</span><span style="">的编码,导致生成一个错误的字符串。要恢复,就要先把字符串恢复成原始字节数组,然后通过正确的编码</span><span style="" lang="EN-US">GB18030</span><span style="">再次解码成字符串(即把以</span><span style="" lang="EN-US">GB18030</span><span style="">编码的数据转成</span><span style="" lang="EN-US">unicode</span><span style="">的字符串)。注意,字符串永远都是</span><span style="" lang="EN-US">unicode</span><span style="">编码的。</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">但编码转换并不是负负得正那么简单,这里我们之所以可以正确地转换回来,是因为</span><span style="" lang="EN-US"> </span><span style="" lang="EN-US">ISO8859-1</span><span style="" lang="EN-US"> </span><span style="">是单字节编码,所以每个字节被按照原样</span><span style="" lang="EN-US"> </span><span style="">转换为</span><span style="" lang="EN-US"> </span><span style="" lang="EN-US">String</span><span style="" lang="EN-US"> </span><span style="">,也就是说,虽然这是一个错误的转换,但编码没有改变,所以我们仍然有机会把编码转换回来!</span><span style="" lang="EN-US"> </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="" lang="EN-US"> </span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; line-height: 16.5pt;" align="left"><span style="">总结:</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal" style="text-align: left; text-indent: 21.0pt; line-height: 16.5pt;" align="left"><span style="">所以,我们在处理</span><span style="" lang="EN-US">java</span><span style="">的编码问题时,要分清楚三个概念:</span><span style="" lang="EN-US">Java</span><span style="">采用的编码:</span><span style="" lang="EN-US">unicode</span><span style="">,</span><span style="" lang="EN-US">JVM</span><span style="">平台默认字符集和外部资源的编码。</span><span style="" lang="EN-US"></span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>

相关推荐

    GBK、UTF-8编码转换工具

    GBK、UTF-8批量文件3秒快速转码工具(支持GBK,UTF-8免费转换),UTF-8/GBK编码在线转换工具,压缩包可以有多目录与文件,如目录中有图片不会转码,但是会随转码好的文件一起打包下载。 使用帮助 . 上传压缩包(仅支持zip...

    c gbk和 utf-8 转换

    GBK和UTF-8是两种广泛使用的字符编码标准,它们各自有不同的特性和应用场景。本文将深入探讨C语言中如何实现GBK与UTF-8的互相转换,同时不依赖任何外部库。 GBK是中国大陆广泛采用的一种汉字编码标准,它是GB2312的...

    UTF-8toGBK_labview编码gbk_LabVIEWUTF-8_utf-8toGbk_

    在IT行业中,字符编码是一个非常重要的概念,尤其是在处理多语言数据和跨平台通信时。UTF-8和GBK是两种常见..."UTF-8toGBK.vi"这个VI提供了一个实用的工具,可以帮助开发者解决在处理中文字符串时可能出现的编码问题。

    C#写的 GBK GB2312 UTF-8转换

    Console.WriteLine("GBK转UTF-8后的字符串:" + utf8Str); } } ``` 在这个例子中,`Encoding.GetEncoding("GBK")`获取了GBK编码器,`GetBytes()`方法将GBK编码的字符串转换为字节数组,然后`Encoding.UTF8....

    GBK与UTF-8转码(C++)

    ### GBK与UTF-8转码(C++) #### 知识点概述 本文将详细介绍如何在Microsoft Foundation Classes (MFC)环境下实现GBK与UTF-8之间的编码转换。该技术适用于那些需要处理不同字符集数据的应用程序开发场景,特别是在...

    eclispe GBK转UTF-8乱码解决

    本文将详细讨论如何在Eclipse中解决GBK转UTF-8乱码的问题,以及介绍一种插件工具来帮助统一编码格式。 首先,我们需要了解GBK和UTF-8编码的区别。GBK是中国大陆的标准汉字编码,它是GB2312的扩展,包含更多的汉字和...

    批量转 GBK 转 UTF-8 gb2312 Iso-8959-1 转 UTF-8

    在标题和描述中提到的“批量转 GBK 转 UTF-8”、“gb2312 转 UTF-8”以及“Iso-8859-1 转 UTF-8”是指将使用这些编码格式的文件转换成UTF-8编码。这是因为UTF-8具有广泛的兼容性和通用性,许多系统和软件默认使用UTF...

    java文件编码GBK转utf8完美解决方案

    idea、Eclipse等项目导入.java文件中文乱码完美解决方案:文件夹下所有GBK编码的.java一键转为utf-8,操作方式:将GBK2UTF8.jar文件考到需要转码项目目录,在当前位置运行控制台,输入命令java -jar GBK2UTF8.jar,...

    GBK转UTF-8

    标题"GBK转UTF-8"指的是将使用GBK编码的文件或工程转换为使用UTF-8编码的过程。下面我们将深入探讨这两种编码以及转换工具的使用。 GBK编码,全称为“汉字内码扩展规范”,是中国大陆广泛使用的多字节字符集,它...

    GBK 与 UTF-8 间编码转换

    描述中提到的“用vs2017写的”小程序就是为了解决这个问题,通过编程实现GBK到UTF-8或者UTF-8到GBK的转换,以提高工作效率。 编码转换的原理通常是读取原始编码的字节流,然后根据目标编码的规则重新分配字节。对于...

    gbk转换utf-8工具!绝对给力!

    5. **转换过程**:GBK转UTF-8工具的工作原理是读取GBK编码的文件,根据GBK的编码规则解析出原始的Unicode码点,然后按照UTF-8的编码规则重新生成字节流,保存为新的UTF-8编码文件。 6. **注意事项**:在进行编码...

    Eclipse项目的GBK编码转为UTF-8插件

    当开发者将一个原本使用GBK编码的Eclipse项目导入到Android Studio时,由于Android Studio默认使用的是UTF-8编码,可能会遇到编码不匹配的问题,导致代码显示乱码或编译错误。解决这个问题通常需要手动逐个文件转换...

    Linux 下批量 gbk 转 utf-8 编码脚本

    Linux 下批量 gbk 转 utf-8 编码脚本

    项目编码修改, GBK 变UTF-8 , 全格式转UTF-8, 编码转utf-8

    请使用这款软件,直接将代码转换为UTF-8 注意: 1、xml不需要转换,因为xml默认是utf-8,在你新建的时候已经是正确的格式了 2、图片更不需要转换 3、bin目录,gen目录的直接忽略 4.只需要src目录的代码转换,请确保...

    pb12 gb转utf-8

    转换“pb12 gb转utf-8”意味着你可能有一个使用PowerBuilder 12编写的程序或数据库,其中的数据是以GBK编码存储的,现在需要将这些数据转换为UTF-8编码以便在更广泛的环境中使用或与其他系统进行兼容。 这个过程...

    转码工具gbk转utf-8,支持批量

    本篇文章将详细解析这两种编码方式以及如何进行转换,特别是针对“转码工具gbk转utf-8,支持批量”这一主题。 首先,GBK(Great Chinese Coding)是一种中文扩展的编码标准,它是GB2312的升级版,包含了更多的汉字...

    批量将Java源代码文件的编码从GBK转为UTF-8

    老项目采用GBK编码格式,而新项目采用的UTF-8编码格式,如果直接把Java源代码复制到Eclipse中所有的中文信息会出现乱码。所以写了个小的方法类,将java文件的编码格式从GBK转UTF-8

    eclipse中class乱码GBK-UTF-8转换工具

    这篇博客文章“eclipse中class乱码GBK-UTF-8转换工具”正是为了解决这个问题。 GBK是GB2312的扩展,包含了更多的汉字和其他字符,主要在中国大陆使用。UTF-8则是一种广泛使用的Unicode编码,支持世界上几乎所有的...

    文件编码转换,GBK和UTF-8双向互换,批处理文化方法

    文件包含:gbk2utf-8.bat、utf-82gbk.bat、iconv.exe及使用说明.txt四个文件 使用说明:1把要转换的所有.h和.c文件拷入该目录下,双击相应的bat文件即可。 2转换结果会保存在utf-8Res或gbkRes目录下。 3如果转换除...

    gbk转Utf8_编码转换_firegbi_GBK转UTF-8_

    总之,GBK到UTF-8的转换是解决中文字符编码问题的关键步骤,尤其在跨平台或与国际化的系统交互时。通过理解编码原理和使用Python的`codecs`模块,我们可以有效地完成这个任务,确保数据的正确性和兼容性。

Global site tag (gtag.js) - Google Analytics