`
gg19861207
  • 浏览: 181722 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

通过“中文”两个字来深度剖析各种不同的编码方式,彻底理解乱码是如何产生的?

阅读更多

最近在和吉林的学生做模拟面试的时候,我一直在问他们一个问题,就是让他们阐述一下乱码的产生过程,回答的不是很透彻,都只是简单的说在配置文件里加个过滤器就行了,感觉只是会机械的使用,不太明白具体的过程,今天写了一篇文章,希望对编码方式有一个更深刻的理解:

编码:把字符转换成计算机能够识别的01序列。
解码:把01序列转换为人类认知的字符。
不同的编码方式最本质的不同就是各种编码方式都有自己独特的字符和字节的对照表

Java用unicode编码,也就是用16位来编写一个字符。
utf8:用三个字节来编码一个中文字符。
.Java源文件(含有中文的话)是用:GBK编码。
.class文件用UTF8编码。例如汉字“中文”被编译成.class文件后,“中”字用
e4:b8:ad来存储;“文”字用e6:96:87来存储。//.class文件可用ue32查看
Java虚拟机内存中是用unicode来编码,unicode用两个字节来表示一个中文字符,在内存当中“中字”用4e:2d来表示;“文”字用65,87来表
示。
字符串在内从中的01序列可以通过下面的方式来输出:
例如:
(一):中文的Unicode编码方式:4e:2d:65,87
public void testEncoding(){
try {
String str = "中文";
byte[] bytes = str.getBytes("UTF-16");
//4e:2d:65:87
log(EncodingUtils.toHexString(bytes));//EncodingUtils为手写的工具类,用来将序列用十六进制(也就是
unicode的编码方式)输出

} catch (Exception e) {
e.printStackTrace();
}
}

技巧:查看中午字符的unicode编码的快捷方式,在Word里输入中文,如“中”字,然后把鼠标放在“中”字的后面,然后按“alt+x”,即可
观察到“中”字的unicode编码方式。

(二):“中文”字符的GBK编码:
//"中文"的GBK编码是:d6:d0:ce:c4
public void testEncoding2(){

try {
//正确的GBK编码的字节流
byte[] bytes = new byte[]{(byte)0xd6,(byte)0xd0,(byte)0xce,(byte)0xc4};

//指定了正确的编码之后,能够转换为正确的unicode编码的字符
String unicodeChar = new String(bytes,"GBK");

log(unicodeChar);//输出结果是“中文”

log("在内存中的编码是:"+EncodingUtils.toHexString(unicodeChar));//输出结果是4e:2d:65:87(为中文的
unicode编码)

} catch (Exception e) {
e.printStackTrace();
}
}

步骤:1:创建一个字符流,该字符流的特点是如果按照GBK对该字符流进行编码,则按照GBK的字符对照表,该字符流对应的字符串是中文,所
以log(unicodeChar)打印出来的是中文。
2:当用Unicode来显示中文的时候,根据unicode的字符对照表,“中文”用unicode的编码的话,则字节流序列为4e:2d:65:87,所以要打印出
“中文”在内存中的字节流表示的话,则为4e:2d:65:87,可见字符是最主要的,字符是不变的,变的是不同编码方式的字节流。

(二):“中文”字符的UTF8编码:
//"中文"的UTF-8编码是:e4:b8:ad:e6:96:87
public void testEncoding3(){

try {
//正确的UTF-8编码的字节流
byte[] bytes = new byte[]{(byte)0xe4,(byte)0xb8,(byte)0xad,(byte)0xe6,(byte)0x96,(byte)0x87};

//指定了正确的编码之后,能够转换为正确的unicode编码的字符
String unicodeChar = new String(bytes,"UTF-8");

log(unicodeChar);//输出结果是“中文”

log("在内存中的编码是:"+EncodingUtils.toHexString(unicodeChar));//输出结果是4e:2d:65:87(为中文的
unicode编码)

} catch (Exception e) {
e.printStackTrace();
}
}
这个的输出结果和分析思路和(二)是一样的,不做赘述。

(四)
//"中文"的UTF-8编码是:e4:b8:ad:e6:96:87
public void testEncoding4(){

try {
//正确的UTF-8编码的字节流
byte[] bytes = new byte[]{(byte)0xe4,(byte)0xb8,(byte)0xad,(byte)0xe6,(byte)0x96,(byte)0x87};

//指定了错误的编码,得到错误的字符
String unicodeChar = new String(bytes,"GBK");//因为GBK编码是两个字节表示一个字符,而现在的bytes是六个
字节,所以得到的就是三个字符,例如假设这时按照GBK的编码对照表,字节流bytes转化为字符的是“涓枃”,所以这时的字符unicodeChar
是涓枃)
log("unicodeChar 是:"EncodingUtils.toHexString(unicodeChar));//输出结果是6d93:e15f:6783:这是字符涓
枃的unicode表示,注意此时的输出结果不是“涓枃”,该语句是打印字节流,而不是字符串

//按原路返回
byte[] nextbytes = unicodeChar.getBytes("GBK");
log(EncodingUtils.toHexString(nextbytes));//输出结果是e4:b8:ad:e6:96:87:,按照原路返回

String nextChar = new String(nextbytes,"UTF-8");

log(unicodeChar);//输出结果是“涓枃”
log("在内存中的编码是:"+EncodingUtils.toHexString(unicodeChar));//输出结果是“在内存中的编码是:
6d93:e15f:6783:”

log("nextChar ->");//输出结果是“中文”
log(nextChar);
log("在内存中的编码是:"+EncodingUtils.toHexString(nextChar));//输出结果是“在内存中的编码是:
4e2d:6587:”
} catch (Exception e) {
e.printStackTrace();
}
}

(五):
public void testEncoding5(){

try {
//String str = "联通";
//正确的UTF-8编码的字节流
byte[] bytes = new byte[]{(byte)0xe8,(byte)0x81,(byte)0x94,(byte)0xe9,(byte)0x80,(byte)
0x9a};//str.getBytes("UTF-8");

log(EncodingUtils.toHexString(bytes));//输出结果是“e8:81:94:e9:80:9a:”

//指定了错误的编码,得到错误的字符
String unicodeChar = new String(bytes,"GBK");//此时unicode的值是“鑱旈??”?说明了在GBK编码中字节
0x80,0x9a根本没有对应的字符,按照GBK的编码方式,根本不知道这两个字节该转换为何种字符,就显示为字符“?”
log(unicodeChar);//显示结果是“鑱旈??”
log("在内存中的编码是:"+EncodingUtils.toHexString(unicodeChar));显示结果是“9471:65c8:fffd:fffd:”
log(unicodeChar.length()+"");//显示结果是"4"
//按原路返回,不一定能够返回!!!
byte[] nextbytes = unicodeChar.getBytes("GBK");
log(EncodingUtils.toHexString(nextbytes));//显示结果是“e8:81:94:e9:3f:3f:”其中3f是GBK中“?”的编码
字节

String nextChar = new String(nextbytes,"UTF-8");



log("nextChar ->");
log(nextChar);//显示结果是“联???”,因为按照e8:81:94还可以显示为“联”,而“e9:3f:3f”在UTF8中已经无
法根据编码表来显示字符了。
log("在内存中的编码是:"+EncodingUtils.toHexString(nextChar));//输出结果是“054:fffd:003f:003f:”,即
是“联???”的unicode编码方式

} catch (Exception e) {
e.printStackTrace();
}
}

总结:最本质的就是编码对照表,即 字符《---------编码规则-------------》字节流,抓住这个主线,一步步分析就行了。

EncodingUtils.java的源代码:
package org.topxp.utils;
/**
* 编码测试工具类
* @author <a href='mailto:tengfei.lee@gmail.com'>Kenny Lee</a>
* <br/>2006-8-1 15:28:05
*/
public class EncodingUtils {
final static char[] digits = {
'0' , '1' , '2' , '3' , '4' , '5' ,
'6' , '7' , '8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
'o' , 'p' , 'q' , 'r' , 's' , 't' ,
'u' , 'v' , 'w' , 'x' , 'y' , 'z'
};

/**
* 返回一个int值的二进制表示的字符串
* @param i
* @return
*/
public static String toBinaryString(int i){
return toUnsignedString(i,32,1);
}

/**
* 返回一个char值的二进制表示的字符串
* @param c
* @return
*/
public static String toBinaryString(char c){
return toUnsignedString(c,16,1);
}

/**
* 返回一个字符串的二进制表示的字符串
* @param s
* @return
*/
public static String toBinaryString(String s){
char[] cs = s.toCharArray();
StringBuffer sb = new StringBuffer();
for(int i=0; i<cs.length; i++){
sb.append(toBinaryString(cs[i]));
sb.append(":");
}
return sb.toString();
}

/**
* 返回一个byte[]的二进制表示的字符串
* @param bs
* @return
*/
public static String toBinaryString(byte[] bs){
StringBuffer sb = new StringBuffer();
for(int i=0; i<bs.length; i++){
sb.append(toBinaryString(bs[i]));
sb.append(":");
}
return sb.toString();
}

/**
* 返回一个byte的二进制表示的字符串
* @param c
* @return
*/
public static String toBinaryString(byte c){
return toUnsignedString(c,8,1);
}

/**
* 返回一个int的十六进制表示的字符串
* @param i
* @return
*/
public static String toHexString(int i){
return toUnsignedString(i,8,4);
}

/**
* 返回一个char的十六进制表示的字符串
* @param c
* @return
*/
public static String toHexString(char c){
return toUnsignedString(c,4,4);
}

/**
* 返回一个byte的十六进制表示的字符串
* @param b
* @return
*/
public static String toHexString(byte b){
return toUnsignedString(b,2,4);
}

/**
* 返回一个String的十六进制表示的字符串
* @param s
* @return
*/
public static String toHexString(String s){
char[] cs = s.toCharArray();
StringBuffer sb = new StringBuffer();
for(int i=0; i<cs.length; i++){
sb.append(toHexString(cs[i]));
sb.append(":");
}
return sb.toString();
}

/**
* 返回一个byte[]的十六进制表示的字符串
* @param bs
* @return
*/
public static String toHexString(byte[] bs){
StringBuffer sb = new StringBuffer();
for(int i=0; i<bs.length; i++){
sb.append(toHexString(bs[i]));
sb.append(":");
}
return sb.toString();
}
private static String toUnsignedString(int i,int length,int shift){
char[] buf = new char[32];
for(int j=0; j<32; j++){
buf[j] = '0';
}
int charPos = 32;
int radix = 1 << shift;
int mask = radix - 1;
do {
buf[--charPos] = digits[i & mask];
i >>>= shift;
} while (i != 0);

return new String(buf,32-length,length);
}
}

分享到:
评论

相关推荐

    彻底解决中文乱码的问题

    然而,每个具体问题可能需要针对性的解决方案,因此在实际工作中,了解并熟练掌握字符编码原理,结合具体情况调整代码,才能真正做到“彻底解决中文乱码问题”。 在提供的压缩包文件“中文乱码的问题决绝.doc”中,...

    Jsp页面中的字符编码方式与乱码解决方法

    JSP 页面中的字符编码方式与乱码解决方法 在 JSP 中,字符编码方式的设置是非常重要的,因为它直接影响着页面的显示效果。如果不正确地设置字符编码方式,可能会出现乱码问题。下面我们将详细介绍 JSP 中的字符编码...

    C# 将中文乱码转换成中文

    当我们在不同的系统或网络环境中传输或存储文本时,可能会遇到字符编码不一致导致的中文乱码问题。本文将深入探讨如何使用C#语言解决中文乱码问题,将乱码文本正确转换为可读的中文。 ### 核心知识点:字符编码与...

    JSP中文编码理论及乱码产生原因及问题解决

    在IT行业中,尤其是在Web开发领域,JSP(JavaServer Pages)是一种常见的...只有深入理解编码机制,才能有效地预防和解决乱码问题,提升用户体验。在实际项目开发中,应时刻关注编码问题,做到早预防、早发现、早解决。

    彻底解决 source_insight 中文注释显示乱码

    在IT行业中,源代码编辑器是开发者不可或缺的工具,Source Insight作为一款强大的源代码阅读和分析工具,受到了很多程序员的青睐。...总之,理解和配置源代码编辑器的字符编码和字体支持是解决中文乱码问题的关键。

    dbf解决中文乱码

    要解决这个问题,你需要知道原始文件的编码方式,并确保读取或写入时使用相同的编码。 2. **编程语言处理**: - **Python**:在Python中,可以使用`pyodbc`、`pandas`或`dbfread`库来处理DBF文件。在读取时,需要...

    乱码问题的解决

    编码不一致可能出现在多个方面,如页面编码、服务器编码、客户端编码、数据库编码等。在本文中,我们将详细介绍乱码问题的原因、类型和解决方法。 一、编码不一致的原因 编码不一致是乱码问题的主要原因。编码不...

    Linux系统中文乱码解决完整方案

    本文档提供了两个解决方案:方法一是使用 Putty 代替 Secure Shell Client,修改窗口 Translation 中的 Received data assumed to be in which character set 值为 UTF-8,从而解决中文乱码问题。方法二是修改 Linux...

    jspdf中文乱码解决方法.zip

    在使用jspdf库生成PDF文档时,经常遇到中文字符显示为乱码的问题。这通常是由于字符编码不兼容或缺少字体支持导致的。本教程将详细解释如何解决jspdf中文乱码的问题,帮助开发者顺利导出含有中文内容的PDF。 首先,...

    jsp 中文乱码 原因及彻底解决办法

    【JSP 中文乱码的原因及解决方法】 在开发基于Java的JSP应用程序时,遇到中文乱码问题是很常见的困扰。...通过深入了解各种字符集的工作原理,以及它们在不同环境下的应用,开发者能够更好地应对和解决中文乱码问题。

    sqlite3 for delphi 解决中文乱码问题

    这里我们将详细探讨SQLite3在Delphi中的应用,中文乱码的成因,以及如何通过自定义修改来解决这个问题。 首先,SQLite3是一个轻量级的嵌入式数据库,广泛应用于各种编程语言,包括Delphi。Delphi提供了与SQLite3...

    Lua文件反编译汉字乱码处理

    在编程领域,有时候我们需要对Lua脚本进行反编译,以便理解其内部逻辑或进行二次开发。然而,当处理含有汉字的Lua文件时,反编译过程中可能会出现...对于开发者来说,理解不同编码间的转换和兼容性是非常重要的技能。

    JSP中文乱码的产生原因及解决方案

    总结来说,解决JSP中文乱码问题的关键在于理解不同环节的字符集设置,并确保它们之间的一致性。从源文件的编码到页面显示、HTTP交互、数据库操作以及文件读写,每个环节都需要正确设置字符集,以确保中文字符能够...

    Selenium WebDriver + ReportNG中文乱码问题

    在生成报告时,ReportNG 使用的是默认的编码方式,而不是 UTF-8 编码方式,这会导致中文字符的乱码。为了解决这个问题,我们需要修改 ReportNG 的源码,特别是 AbstractReporter 类中的 generateFile 方法。 在 ...

    FORM表单中文乱码问题分析与解决

    FORM表单中文乱码问题分析与解决 在 Web 开发中,中文乱码问题...对中文的编码方式和解码方式是解决中文乱码问题的关键。使用正确的编码方式和解码方式可以解决中文乱码问题,从而确保FORM表单中的中文参数传递正确。

    乱码,编码转换器

    通过理解不同编码系统的特性和使用合适的转换工具,我们可以确保信息的准确传递,避免因为编码不匹配而产生的误解。在实际使用中,尤其是在处理涉及多语言或多系统交互的项目时,这样的工具显得尤为关键。

    乱码 编码方式解决 gbk ISO8859-1 utf8 编码

    ### 乱码问题与编码方式解决方案 ...通过上述方法,可以有效地解决由GBK、ISO 8859-1、UTF-8等不同编码方式引起的乱码问题。在实际应用中,还需要根据具体情况选择最合适的解决方案,确保数据的正确性和一致性。

Global site tag (gtag.js) - Google Analytics