`
星海孤舟
  • 浏览: 68916 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
社区版块
存档分类
最新评论

Java基础——孙鑫谈Java中文乱码问题产生原因分析(一)

阅读更多
Java基础——孙鑫谈Java中文乱码问题产生原因分析(一)
作者:孙鑫   来源:孙鑫   发布者:admin
时间:2009-07-22 10:48:07   点击:1689

 

在计算机中,只有二进制的数据,不管数据是在内存中,还是在外部存储设备上。对于我们所看到的字符,也是以二进制数据的形式存在的。不同字符对应二进制数的规则,就是字符的编码。字符编码的集合称为字符集。

常用字符集

在早期的计算机系统中,使用的字符非常少,这些字符包括26个英文字母、数字符号和一些常用符号(包括控制符号),对这些字符进行编码,用1个字节就足够了(1个字节可以表示28=256种字符)。然而实际上,表示这些字符,只使用了1个字节的7位,这就是ASCII编码1.ASCII

ASCII(American Standard Code for Information Interchange,美国信息互换标准代码),是基于常用的英文字符的一套电脑编码系统。每一个ASCII码与一个8位(bit)二进制数对应。其最高位是0,相应的十进制数是0~127。例如,数字字符“0”的编码用十进制数表示就是48。另有128个扩展的ASCII码,最高位都是1,由一些图形和画线符号组成。ASCII是现今最通用的单字节编码系统。

ASCII用一个字节来表示字符,最多能够表示256种字符。随着计算机的普及,许多国家都将本地的语言符号引入到计算机中,扩展了计算机中字符的范围,于是就出现了各种不同的字符集。

2.ISO8859-1

因为ASCII码中缺少£、ü和许多书写其他语言所需的字符,为此,可以通过指定128以后的字符来扩展ASCII码。国际标准组织(ISO)定义了几个不同的字符集,它们是在ASCII码基础上增加了其他语言和地区需要的字符。其中最常用的是ISO8859-1,通常叫做Latin-1。Latin-1包括了书写所有西方欧洲语言不可缺少的附加字符,其中0~127的字符与ASCII码相同。ISO 8859另外定义了14个适用于不同文字的字符集(8859-2到8859-15)。这些字符集共享0~127的ASCII码,只是每个字符集都包含了128~255的其他字符。

3.GB2312和GBK

GB2312是中华人民共和国国家标准汉字信息交换用编码,全称《信息交换用汉字编码字符集-基本集》,标准号为GB2312-80,是一个由中华人民共和国国家标准总局发布的关于简化汉字的编码,通行于中国大陆和新加坡,简称国标码。

因为中文字符数量较多,所以采用两个字节来表示一个字符,分别称为高位和低位。为了和ASCII码有所区别,中文字符的每一个字节的最高位都用1来表示。GB2312字符集是几乎所有的中文系统和国际化的软件都支持的中文字符集,也是最基本的中文字符集。它包含了大部分常用的一、二级汉字和9区的符号,其编码范围是高位0xa1-0xfe,低位也是0xa1-0xfe,汉字从0xb0a1开始,结束于0xf 7fe。

为了对更多的字符和符号进行编码,由前电子部科技质量司和国家技术监督局标准化司于1995年12月颁布了GBK(K是“扩展”的汉语拼音第一个字母)编码规范,在新的编码系统里,除了完全兼容GB2312外,还对繁体中文、一些不常用的汉字和许多符号进行了编码。它也是现阶段Windows和其他一些中文操作系统的默认字符集,但并不是所有的国际化软件都支持该字符集。不过要注意的是GBK不是国家标准,它只是规范。GBK字符集包含了20 902个汉字,其编码范围是0x8140-0xfefe。

每个国家(或区域)都规定了计算机信息交换用的字符编码集,这就造成了交流上的困难。想像一下,你发送一封中文邮件给一位远在西班牙的朋友,当邮件通过网络发送出去的时候,你所书写的中文字符会按照本地的字符集GBK转换为二进制编码数据,然后发送出去。当你的朋友接收到邮件(二进制数据)后,查看信件时,会按照他所用系统的字符集,将二进制编码数据解码为字符,然而由于两种字符集之间编码的规则不同,导致转换出现乱码。这是因为,在不同的字符集之间,同样的数字可能对应了不同的符号,也可能在另一种字符集中,该数字没有对应符号。

为了解决上述问题,统一全世界的字符编码,由Unicode协会制定并发布了Unicode编码。

4.Unicode

Unicode(统一的字符编码标准集)使用0~65 535的双字节无符号数对每一个字符进行编码。它不仅包含来自英语和其他西欧国家字母表中的常见字母和符号,也包含来自古斯拉夫语、希腊语、希伯来语、阿拉伯语和梵语的字母表。另外还包含汉语和日语的象形汉字和韩国的Hangul音节表。

目前已经定义了40 000多个不同的Unicode字符,剩余25 000个空缺留给将来扩展使用。其中大约20 000个字符用于汉字,另外11 000左右的字符用于韩语音节。Unicode中0~255的字符与ISO8859-1中的一致。

Unicode编码对于英文字符采取前面加“0”字节的策略实现等长兼容。如“a”的ASCII码为0x61,Unicode码就为0x00,0x61。

5UTF-8

使用Unicode编码,一个英文字符要占用两个字节,在Internet上,大多数的信息都是用英文来表示的,如果都采用Unicode编码,将会使数据量增加一倍。为了减少存储和传输英文字符数据的数据量,可以使用UTF-8编码。

UTF-8全称是Eight-bit UCS Transformation Format(UCS,Universal Character Set,通用字符集,UCS是所有其他字符集标准的一个超集)。对于常用的字符,即0~127的ASCII字符,UTF-8用一个字节来表示,这意味着只包含7位ASCII字符的字符数据在ASCII和UTF-8两种编码方式下是一样的。如果字符对应的Unicode码是0x0000,或在0x0080与0x007f之间,对应的UTF-8编码是两个字节,如果字符对应的Unicode码在0x0800与0xffff之间,对应的UTF-8编码是三个字节。因为中文字符的Unicode编码在0x0800与0xffff之间,所以数据如果是中文,采用UTF-8编码数据量会增加50%。

Unicode与UTF-8转换的规则简述如下:

(1)如果Unicode编码的16位二进制数的前9位是0,则UTF-8编码用1个字节来表示,这个字节的首位是“0”,剩下的7位与原二进制数据的后7位相同。例如:

Unicode编码:\u0061 = 00000000 01100001

UTF-8编码:01100001 = 0x61

(2)如果Unicode编码的16位二进制数的头5位是0,则UTF-8编码用2个字节来表示,首字节以“110”开头,后面的5位与原二进制数据除去前5个零后的最高5位相同;第二个字节以“10”开头,后面的6位与原二进制数据中的低6位相同。例如:

Unicode编码:\u00A9 = 00000000 10101001

UTF-8编码:11000010 10101001 = 0xC2 0xA9

(3)如果不符合上述两个规则,则用三个字节表示。第一个字节以“1110”开头,后四位为原二进制数据的高四位;第二个字节以“10”开头,后六位为原二进制数据中间的六位;第三个字节以“10”开头,后六位为原二进制数据的低六位。例如:

Unicode编码:\u4E2D = 01001110 00101101

UTF-8编码:11100100 10111000 10101101 = 0xE4 0xB8 0xAD

在UTF-8编码的多字节串中,第一个字节开头“1”的数目就是整个字符串中字节的数目。

17.1.2 对乱码产生过程的分析

为了让使用Java语言编写的程序能在各种语言的平台下运行,Java在其内部使用Unicode字符集来表示字符,这样就存在Unicode字符集和本地字符集进行转换的过程。当在Java中读取字符数据的时候,需要将本地字符集编码的数据转换为Unicode编码,而在输出字符数据的时候,则需要将Unicode编码转换为本地字符集编码。

例如,在中文系统下,从控制台读取一个字符“中”,实际上读取的是“中”的GBK编码0xD6D0,在Java语言中要将GBK编码转换为Unicode编码0x4E2D,此时,在内存中,字符“中”对应的数值就是0x4E2D,当我们向控制台输出字符时,Java语言将Unicode编码再转换为GBK编码,输出到控制台,中文系统再根据GBK字符集画出相应的字符。

从上述过程来看,读取和写入的过程是可逆的,那么理应不会出现中文乱码问题。然而,实际应用的情形,比上述过程要复杂得多。在Web应用中,通常都包括了浏览器、Web服务器、Web应用程序和数据库等部分,每一部分都有可能使用不同的字符集,从而导致字符数据在各种不同的字符集之间转换时,出现乱码的问题。

在Java语言中,不同字符集编码的转换,都是通过Unicode编码作为中介来完成的。例如,GBK编码的字符“中”要转换为ISO-8859-1(ISO8859-1)编码,其过程如下:

(1)因为在Java中的字符,都是用Unicode来表示的,所以GBK编码的字符“中”要转换为Unicode表示:0xD6D0->0x4E2D。

(2)将字符“中”的Unicode编码转换为ISO-8859-1编码,因为Unicode编码0x4E2D在ISO-8859-1中没有对应的编码,于是得到0x3f,也就是字符“?”。

下面的代码演示了这一过程:

//GBK编码的字符“中”转换为Unicode编码表示

String str="中";

//将字符“中”的Unicode编码转换为ISO-8859-1编码

byte[] b=str.getBytes("ISO-8859-1");

 

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

{

       //输出转换后的二进制代码。

       System.out.print(b[i]);

}

当从Unicode编码向某个字符集转换时,如果在该字符集中没有对应的编码,则得到0x3f(即问号字符?)。这就是为什么有时候我们输入的是中文,在输出时却变成了问号。

从其他字符集向Unicode编码转换时,如果这个二进制数在该字符集中没有标识任何的字符,则得到的结果是0xfffd例如一个GBK的编码值0x8140,从GB2312向Unicode转换,然而由于0x8140不在GB2312字符集的编码范围(0xa1a1-0xfefe),当然也就没有对应任何的字符,所以转换后会得到0xfffd。下面的代码演示了这一过程。

//构造一个二进制数据。

byte[] buf={(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1};

//将二进制数据按照GB2312向Unicode编码转换。

String str=new String(buf,"GB2312");

       

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

{

    //取出字符串中的每个Unicode编码的字符。

    char ch=str.charAt(i);

    //将该字符对应的Unicode编码以十六进制的形式输出。

    System.out.print(Integer.toHexString((int)ch));

    System.out.print("--");

    //输出该字符。

    System.out.println(ch);

}

             转载请注明出处:程序员之家 http://www.sunxin.org

分享到:
评论

相关推荐

    Java关于中文乱码问题的多种解决方法

    在Java编程中,中文乱码问题是一个常见的困扰,尤其是在处理文件读写、网络传输或数据库操作时。本文将深入探讨几种解决Java中中文乱码问题的方法,并以MyEclipse为开发环境,结合实际示例进行讲解。 1. 文件读写中...

    java中文乱码之解决URL中文乱码问题的方法

    在Java开发中,遇到中文乱码问题是一种常见的挑战,特别是在处理URL时。URL中文乱码问题主要是由于URL编码和解码过程中的不一致导致的。下面将详细介绍如何解决这个问题,并探讨几种常用的方法。 首先,我们需要...

    Java生成pdf文件,解决中文乱码问题.pdf

    Java 生成 PDF 文件,解决中文乱码问题 Java 生成 PDF 文件,解决中文乱码问题是 Java 编程中常见的问题。解决这个问题的关键是正确地设置中文字体,以避免乱码问题。本文将通过一个完整的示例代码,详细讲解如何...

    java中文乱码解决问题

    JAVA 中文乱码问题是开发过程中常见的问题之一,解决这个问题需要了解乱码产生的原因,然后对症下药。下面我们对容易产生乱码问题的场景进行分析,并提出解决方案。 1. 以 POST 方法提交的表单数据中有中文字符 在...

    java中文乱码问题详解--- java中文乱码问题详解

    ### Java中文乱码问题详解 #### 一、中文问题的来源与背景 计算机技术发展初期,操作系统主要支持单字节的ASCII字符集。随着全球化进程加快和技术进步,为支持多种语言,尤其是双字节编码的语言(如中文),提出了...

    java读写csv文件,中文乱码问题

    下面我们将深入探讨如何在Java中正确地读取和写入CSV文件,以及解决中文乱码的问题。 1. **字符编码的理解**: - 在处理中文字符时,必须确保使用正确的字符编码,例如UTF-8。UTF-8是一种广泛支持的编码格式,可以...

    java aspose word 上传到服务器 导出的word 中文字体乱码 的问题 java aspose

    java aspose word 上传到服务器 导出的word 中文字体乱码 的问题 java aspose java aspose word 上传到服务器 导出的word 中文字体乱码 的问题 java aspose java aspose word 上传到服务器 导出的word 中文字体乱码 ...

    Java Http请求传json数据乱码问题的解决

    在实际解决乱码问题的过程中,如果发现使用Base64加密传输中文数据还会出现问题,可以改用URL编码的方式来传输数据,即使用JavaScript的`encodeURI`函数对数据进行两次URL编码,后端接收到后进行一次URL解码即可。...

    java Socket与C#通信中中文乱码问题的解决方案

    在Java和C#之间进行Socket通信时,遇到中文乱码问题主要是由于编码格式不一致导致的。Java默认使用UTF-8编码,而C#在处理字符串时可能使用其他编码,如GBK或ASCII。为了解决这个问题,我们需要确保两端在发送和接收...

    java中文乱码问题

    Java 中文乱码问题是一个老生常谈的问题,特别是在 Web 应用中。今天,我们将从编码角度分析 Java 编译后在控制台和 Web 等终端显示乱码问题。 一、 Java 处理字符的原理 Java 使用 UNICODE 来存储字符数据,处理...

    java压缩zip文件解决中文乱码问题

    总结来说,处理Java中ZIP文件的中文乱码问题,关键在于明确指定字符集,通常是UTF-8,无论是使用Java内置的API还是第三方库。同时,理解文件系统的编码和压缩/解压过程中的字符编码转换也非常重要。通过这种方式,...

    java zip解压中文乱码问题

    网上很多描述java解压中文乱码的问题,很多描述不全.由于工作需要整理出一个完整版.简单实用.下载后请从ZipUtil.java的main方法开始,一目了然. public static void main(String args[]) { new ZipUtil().unZip("E:\\...

    java中文乱码问题解决

    本文将深入分析Java中文乱码问题的根本原因,介绍各种编码格式的区别和应用场景,并提供解决乱码问题的方法和经验。 在Java中,常见的编码格式有: * ASCII码:总共有128个,用一个字节的低7位表示,0~31是控制...

    中文乱码问题分析 自己总结的

    中文乱码问题是 Java 和 JSP 开发中的一种常见问题,主要是由于 Java 和 JSP 源文件的保存方式是基于字节流的,而编译成 class 文件过程中,使用的编码方式与源文件的编码不一致所致。在 Java 文件中,尽量不要写...

    JAVA反编译文件解决中文乱码

    本文将详细介绍如何解决JAVA反编译文件时的中文乱码问题。 首先,理解问题的根源:Java源代码默认采用UTF-8编码,但某些情况下,源代码可能使用了其他编码方式,如GBK。当这些源代码被编译成字节码后,再用不支持...

    彻底解决中文乱码的问题

    在IT行业中,尤其是在Java编程领域,中文乱码问题是一个常见的挑战。这主要涉及到字符编码的处理,涉及到Unicode、GBK、UTF-8等不同编码格式之间的转换和一致性问题。本篇文章将深入探讨这个问题,并提供一种彻底...

    Java 中文乱码问题

    在Java编程中,中文乱码问题是一个常见的困扰开发者的问题,特别是在处理输入输出或者网络通信时。这个问题涉及到字符编码的理解和正确使用。以下是对这个主题的详细解析: 首先,我们需要了解字符编码的基础知识。...

    java解决中文乱码问题

    Java 解决中文乱码问题 Java 中文乱码问题是中国程序员无法避免的话题。乱码的出现是由于中文和英文的编码格式不同,解码也是不一样的。如果中国的程序员不会遇到乱码,那么只有使用汉语编程。Han语编程是怎么回事...

Global site tag (gtag.js) - Google Analytics