`
raozhiyong11
  • 浏览: 138941 次
  • 性别: Icon_minigender_1
  • 来自: 江西
社区版块
存档分类
最新评论

计算机编码知识和乱码解决

阅读更多

本篇我们来讨论一下struts的国际化编程问题,即所谓的i18n编程问题,这一篇我们讨论其基础部分。与这个问题紧密相关的是在各java论坛中被频繁提及的中文乱码问题,因为,英、美编程人员较少涉及到中文乱码问题,因此,这方面的英文资料也是非常奇缺的,同时也很少找到这方面比较完整的中文资料,本文也尝试对中文乱码问题做一些探讨。要解决上述问题,需要有一定的字符集方面的知识,下面,我们就先介绍字符集的有关情况:

一、从ASCII到Unicode(UTF-8)

电子计算机技术是从美国开始发展起来的,因为美国使用的文字为英文,美国规定的计算机信息交换用的字符编码集是人们熟知的扩展ASCII码,它以8bit字节为单位存储,ASCII的0-31及127为控制符,32-126为可见字符,包括所有的英文字母,阿拉伯数字和其他一些常见符号,128-255的ASCII码则没有定义。

ASCII对英语国家是够用了,但对其他西欧国家却不够用,因此,人们将ASCII扩展到0-255的范围,形成了ISO-8859-1字符集。值得一提的是,因为考虑到程序中处理的信息大多是西文信息,因此有些WEB容器(如:Tomcat4.x)在处理所接收到的request字符串时,如果您没指定request的编码方式则系统就缺省地采用ISO-8859-1,明白这一点对理解后面的问题会有帮助。

相比西方的拼音文字,东方的文字(如中文)的字符数要大得多,根本不可能在一个字节内将它们表示出来,因此,它们以两个字节为单位存储,以中文国标字符集GB2312为例,它的第一个字节为128-255。系统可以据此判断,若第一个字节大于127,则把与该字节后紧接着的一个字节结合起来共两个字节组成一个中文字符。这种由多个字节存储一个字符的字符集叫多字节字符集(MultiByte Charsets),对应的象ASCII这种用一个字节存储一个字符的字符集叫单字节字符集(SingleByte Charsets)。在GB2312字符集中,ASCII字符仍然用一个字节存储,换句话说该ASCII是该字符集的子集。

GB2312只包含数千个常用汉字,往往不能满足实际需要,因此,人们对它进行扩展,这就有了我们现在广泛使用的GBK字符集,GBK是现阶段Windows及其他一些中文操作系统的缺省字符集。它包含2万多个字符,除了保持和GB2312兼容外,还包含繁体中文字,日文字符和朝鲜字符。值得注意的是GBK只是一个规范而不是国家标准,新的国家标准是GB18030-2000,它是比GBK包含字符更多的字符集。

我国的台湾地区使用的文字是繁体字,其字符集是BIG5,而日本采用的字符集则是SJIS。它们的编码方法与GB2312类似,它们的ASCII字符部分是兼容的,但扩展部分的编码则是不兼容的,比如这几种字符集中都有"中文"这两个字符,但他们在各自的字符集中的编码并不相同,这就是用GB2312写成的网页用BIG5浏览时,看到的是乱糟糟的信息的原因。

可见,在字符集的世界里,呈现给我们的是一个群雄割据的局面,各字符集拥有一块自己的地盘。这给各国和各地区交换信息带来了很大的困难,同时,也给国际化(本地化)编程造成了很大的麻烦。

常言道:"分久必合",随着国际标准ISO10646定义的通用字符集(Universal Character Set即UCS)的出现,使这种局面发生了彻底的改观。UCS 是所有其他字符集标准的一个超集. 它保证与其他字符集是双向兼容的. 就是说, 如果你将任何文本字符串翻译到 UCS格式, 然后再翻译回原编码, 你不会丢失任何信息。UCS 包含了用于表达所有已知语言的字符。不仅包括拉丁语、希腊语、 斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语和乔治亚语的描述、还包括中文、 日文和韩文这样的象形文字、 以及平假名、片假名、 孟加拉语、 旁遮普语果鲁穆奇字符(Gurmukhi)、 泰米尔语、印.埃纳德语(Kannada)、Malayalam、泰国语、 老挝语、 汉语拼音(Bopomofo)、Hangul、 Devangari、Gujarati、Oriya、Telugu 以及其他数也数不清的语。对于还没有加入的语言, 由于正在研究怎样在计算机中最好地编码它们, 因而最终它们都将被加入。

ISO 10646 定义了一个 31 位的字符集。 然而, 在这巨大的编码空间中, 迄今为止只分配了前 65534 个码位 (0x0000 到 0xFFFD)。 这个 UCS 的 16位子集称为 基本多语言面 (Basic Multilingual Plane, BMP)。 将被编码在 16 位 BMP 以外的字符都属于非常特殊的字符(比如象形文字), 且只有专家在历史和科学领域里才会用到它们。

UCS 不仅给每个字符分配一个代码, 而且赋予了一个正式的名字。 表示一个 UCS 值的十六进制数, 通常在前面加上 "U+", 就象 U+0041 代表字符"拉丁大写字母A"。 UCS 字符 U+0000 到 U+007F 与 US-ASCII(ISO 646) 是一致的, U+0000 到 U+00FF 与 ISO 8859-1(Latin-1) 也是一致的。这里要注意的是它是以16bit为单位存储,即便对字母"A"也是用16bit,这是与前面介绍的所有字符集不同的地方。

历史上,在国际标准化组织研究ISO10646标准的同时,另一个由多语言软件制造商组成的协会也在从事创立单一字符集的工作,这就是现在人们熟知的Unicode。幸运的是,1991年前后ISO10646和Unicode的参与者都认识到,世界上不需要两个不同的单一字符集。他们合并双方的工作成果,并为创立单一编码表而协同工作。两个项目仍都存在并独立地公布各自的标准,都同意保持ISO10646和Unicode的码表兼容,并紧密地共同调整任何未来的扩展。这与当年在PC机上的操作系统MS-dos与PC-dos的情形有些相象。后面,我们将视ISO10646和Unicode为同一个东西。

有了Unicode,字符集问题接近了完美的解决,但不要高兴得过早。由于历史的原因:一些操作系统如:Unix、Linux等都是基于ASCII设计的。此外,还有一些数据库管理系统软件如:Oracle等也是围绕ASCII来设计的(从其8i的白皮书上介绍的设置系统字符集和字段的字符集中可以间接地看到这一点)。在这些系统中直接用Unicode会导致严重的问题。用这些编码的字符串会包含一些特殊的字符, 比如 '\0' 或 '/', 它们在 文件名和其他 C 库函数参数里都有特别的含义。 另外, 大多数使用 ASCII 文件的 UNIX 下的工具, 如果不进行重大修改是无法读取 16 位的字符的。 基于这些原因, 在文件名, 文本文件, 环境变量等地方,直接使用Unicode是不合适的。

在 ISO 10646-1 Annex R 和 RFC 2279 里定义的 UTF-8 (Unicode Transformation Form 8-bit form)编码没有这些问题。

UTF-8 有以下一些特性:

UCS 字符 U+0000 到 U+007F (ASCII) 被编码为字节 0x00 到 0x7F (ASCII 兼容)。 这意味着只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 两种编码方式下是一样的。

所有 >U+007F 的 UCS 字符被编码为一个多个字节的串, 每个字节都有标记位集。 因此,ASCII 字节 (0x00-0x7F) 不可能作为任何其他字符的一部分。

表示非 ASCII 字符的多字节串的第一个字节总是在 0xC0 到 0xFD 的范围里, 并指出这个字符包含多少个字节。 多字节串的其余字节都在 0x80 到 0xBF 范围里。 这使得重新同步非常容易, 并使编码无国界,且很少受丢失字节的影响。

UTF-8 编码字符理论上可以最多到 6 个字节长, 然而 16 位 BMP 字符最多只用到 3 字节长。

字节 0xFE 和 0xFF 在 UTF-8 编码中从未用到。

通过,UTF-8这种形式,Unicode终于可以广泛的在各种情况下使用了。在讨论struts的国际化编程之前,我们先来看看我们以前在jsp编程中是怎样处理中文问题以及我们经常遇到的:

 

二、中文字符乱码的原因及解决办法

java的内核是Unicode的,也就是说,在程序处理字符时是用Unicode来表示字符的,但是文件和流的保存方式是使用字节流的。在java的基本数据类型中,char是Unicode的,而byte是字节,因此,在不同的环节java要对字节流和char进行转换。这种转换发生时如果字符集的编码选择不当,就会出现乱码问题。

我们常见的乱码大致有如下几种情形:
1、汉字变成了问号"?"
2、有的汉字显示正确,有的则显示错误
3、显示乱码(有些是汉字但并不是你预期的)
4、读写数据库出现乱码

下面我们逐一对它们出现的原因做一些解释:

首先,我们讨论汉字变成问号的问题。

Java中byte与char相互转换的方法在sun.io包中。其中,byte到char的常用转换方法是:
public static ByteToCharConverter getConverter(String encoding);

为了便于大家理解,我们先来做一个小实验:比如,汉字"你"的GBK编码为0xc4e3,其Unicode编码是\u4f60。我们的实验是这样的,先有一个页面比如名为a_gbk.jsp输入汉字"你",提交给页面b_gbk.jsp。在b_gbk.jsp文件中以某种编码方式得到"你"的字节数组,再将该数组以某种编码方式转换成char,如果得到的char值是0x4f60则转换是正确的。

a_gbk.jsp的代码如下:

 

<%@ page contentType="text/html; charset=GBK" language="java" import="java.sql.*" errorPage="" %>
<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">
  <tr>
    <td>&nbsp;</td>
    <td class="bigword">&nbsp;</td>
    <td>&nbsp;</td>
  </tr>
  <tr>
    <td width="100">&nbsp;</td>
    <td class="bigword">Input</td>
    <td width="100">&nbsp;</td>
  </tr>
  <tr>
    <td>&nbsp;</td>
    <td class="bigword">&nbsp;</td>
    <td>&nbsp;</td>
  </tr>
</table>
<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">
  <tr>
    <td><form method="post" action="b_gbk.jsp">
        <table width="611" border="0" cellpadding="0" cellspacing="0">
          <tr>
            <td width="100" align="right"></td>
            <td><input name="ClsID" type="text" class="word" id="ClsID" maxlength="2" >
              *</td>
          </tr>
          <tr>
            <td width="100" align="right">&nbsp;</td>
            <td><input name="btn" type="submit" value="OK">
             </td>
          </tr>
        </table>
      </form></td>
  </tr>
</table>



b_gbk.jsp的代码如下:

<%@ page contentType="text/html; charset=GBK" import="sun.io.*,java.util.*" %>
<%
String a=(String)request.getParameter("ClsID");
byte b[]=a.getBytes("ISO8859-1");
for(int j=0;j<b.length;j++){
  out.println(Integer.toHexString(b[j])+"<br>");
}
ByteToCharConverter convertor=ByteToCharConverter.getConverter("GBK");
char[] c=convertor.convertAll(b);
out.println("b length:"+b.length+"<br>");
out.println("c length:"+c.length+"<br>");
for(int i=0;i<c.length;i++){
 	out.println(Integer.toHexString(c[i])+"<br>");
}
String a1=new String(a.getBytes("ISO8859-1"),"GBK");
%>
<%="a是:"+a%><br>
<%="a1是:"+a1%>

 



在浏览器中打开a_gbk.jsp并输入一个"你"字,点击OK按钮提交表单,则会出现如图1所示的结果:



图1 (见附件)

从图1可以看出,在b_gbk.jsp中这样将byte转换为char是正确的,即得到的char是\u4f60。这里要注意的是:byte b[]=a.getBytes("ISO8859-1");中的编码是ISO8859-1,这就是我们前面提到的有些web容器在您没有指定request的字符集时它就采用缺省的ISO8859-1。

从图1中我们还看到表达式

<!--a是:"+-->

中的a并没有正确地显示"你"而是变成"??"这是什么原因呢?这里的a是作为一个String被显示的,我们来看看我们常用的String构造函数:

String(byte[] bytes,String encoding);

在国标平台上,该函数会认为bytes是按GBK编码的,如果后一个参数省略,它也会认为是encoding是GBK。

对前一个参数就相当于将b_gbk.jsp文件的这句byte b[]=a.getBytes("ISO8859-1");中的ISO8859-1改为GBK,这样显然在GBK字符集中找不到相应的目的编码,它给出的结果是0x3f、0x3f。因此,就会显示为"??",这也就是造成乱码的第一种现象的原因。我们的例子是演示的从byte到char的转换过程,相反的过程也会造成同样的问题,限于篇幅,就不在此讨论了,大家自己可以做类似的实验来验证。

解决该问题的方法就是象例子中a1那样,在获取byte数组时,指定编码为ISO8859-1。

接下来,我们讨论有些汉字能正常显示,有些不能正常显示的问题。

如果我们将String a1=new String(a.getBytes("ISO8859-1"),"GBK");中的GBK改为GB2312则象朱镕基的"镕"字就不能正常显示,这是因为该字是GBK中的字符而在GB2312中不存在。

解决上述两种问题的方法就是象a1那样构造String,也就是人们常说的同时也是常用的转码的方法。采用这种方法会在程序中到处出现这种语句,特别是在Struts中,Struts有一个回写表单的功能,在回写时也要做这种转换,这样的语句差不多要多一倍。因此,这是个比较笨拙的方法,有没有简捷一些的方法呢?其实是有的,只要在取得request的字符串前加上request.setCharacterEncoding("GBK");这句,指定request的字符集。则

<!--a是:"+-->

中的a就能正常显示,a1反而不能正常显示。此时要将byte b[]=a.getBytes("ISO8859-1");中的ISO8859-1变成GBK,从byte到char的转换才是正确的,这就是此时a能正常显示而a1反而不能正常显示的原因。如果此时要a1正常显示则必须将String a1=new String(a.getBytes("ISO8859-1"),"GBK");中的ISO8859-1改为GBK。

很显然,使用request.setCharacterEncoding("GBK");只能解决GBK字符问题,要解决i18n问题则要使用UTF-8来取代GBK。我们接着做上述实验,将a_gbk.jsp和b_gbk.jsp分别另存为a.jsp和b.jsp将文件中的GBK改为UTF-8,更改后的代码分别如下:

a.jsp代码:

<%@ page contentType="text/html; charset=UTF-8" language="java" import="java.sql.*" errorPage="" %>

<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">
  <tr>
    <td>&nbsp;</td>
    <td class="bigword">&nbsp;</td>
    <td>&nbsp;</td>
  </tr>
  <tr>
    <td width="100">&nbsp;</td>
    <td class="bigword">Input</td>
    <td width="100">&nbsp;</td>
  </tr>
  <tr>
    <td>&nbsp;</td>
    <td class="bigword">&nbsp;</td>
    <td>&nbsp;</td>
  </tr>
</table>
<table width="611" border="0" align="center" cellpadding="0" cellspacing="0">
  <tr>
    <td><form method="post" action="b.jsp">
        <table width="611" border="0" cellpadding="0" cellspacing="0">
          <tr>
            <td width="100" align="right"></td>
            <td><input name="ClsID" type="text" class="word" id="ClsID" maxlength="2" >
              *</td>
          </tr>
          <tr>
            <td width="100" align="right">&nbsp;</td>
            <td><input name="btn" type="submit" value="OK">
             </td>
          </tr>
        </table>
      </form></td>
  </tr>
</table>
b.jsp代码:
<ccid_nobr>
<table width="400" border="1" cellspacing="0" cellpadding="2" 
 bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center">
<tr>
    <td bgcolor="e6e6e6" class="code" style="font-size:9pt">
    <pre><ccid_code>  <%@ page contentType="text/html; charset=UTF-8" import="sun.io.*,java.util.*" %>

<%
request.setCharacterEncoding("UTF-8");
String a=(String)request.getParameter("ClsID");
byte b[]=a.getBytes("UTF-8");
for(int j=0;j<b.length;j++){
  out.println(Integer.toHexString(b[j])+"<br>");
}
ByteToCharConverter convertor=ByteToCharConverter.getConverter("UTF-8");
char[] c=convertor.convertAll(b);
out.println("b length:"+b.length+"<br>");
out.println("c length:"+c.length+"<br>");
for(int i=0;i<c.length;i++){
  out.println(Integer.toHexString(c[i])+"<br>");
}
String a1=new String(a.getBytes("UTF-8"),"UTF-8");
%>
<%="a是:"+a%><br>
<%="a1是:"+a1%>



再在a.jsp中输入"你"字,你会发现显示结果中,一个汉字是用三个byte表示的,它们的值分别是0xe4、0xbd、0xa0,也就是说用UTF-8来表示汉字,每个汉字要比GBK多占用一个byte,这也是使用UTF-8要多付出的一点代价吧。

现在,我们讨论一下第三个问题,即显示乱码,有些莫名其妙的汉字并不是你预期的结果。

在上例中将String a1=new String(a.getBytes("UTF-8"),"UTF-8");改为String a1=new String(a.getBytes("UTF-8"),"GBK");再输入"你"字,则a1会显示成"浣?",您只要看一看"浣"的UTF-8码和GBK码就会知道其中的奥秘了。

下面,我们讨论一下最后一个问题,就是读写数据库时出现乱码。

现在一些常用的数据库都支持数据库encoding,也就是说在创建数据库时可以指定它自己的字符集设置,数据库数据以指定的编码形式存储。当应用程序访问数据库时,在入口和出口处都会有encoding转换。如果,在应用程序中字符本来已变成了乱码,当然也就无法正确地转换为数据库的字符集了。数据库的encoding可根据需要来设置,比如要支持简、繁体中文、日、韩、英语选GBK,如果还要支持其他语言最好选UTF-8。

本篇文章对字符集及中文乱码问题做了一下探讨,为实现国际化编程的实践打下一个基础。下一篇文章,我们将介绍struts中实现国际化编程的具体步骤,并将我们前面介绍的登录例子进行国际化。

参考文献:

UTF-8 and Unicode FAQ

《JSP动态网站技术入门与提高》太阳工作室 孙晓龙 赵莉编著

本文作者:罗会波 湖北省当阳市国税局信息中心 可通过 lhbf@sina.com与他联系

(T111)

  • 大小: 17.7 KB
分享到:
评论

相关推荐

    乱码解决 乱码解决 乱码解决 乱码解决 乱码解决

    这通常需要一定的计算机知识,且风险较高,操作需谨慎。 5. **重传或恢复文件**:如果是由于网络传输或文件损坏导致的乱码,尝试重新下载或恢复文件。 三、预防乱码的策略 1. **统一编码标准**:在项目开发中,...

    日文乱码解决利器

    这款“解决利器”软件能解决这个问题,使得程序员无需更改系统设置或具备专业的编码知识,也能顺畅地阅读和工作。 “apploc.msi”是Windows Installer包文件,它很可能包含了这个“日文乱码解决利器”的安装程序。...

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

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

    关于java中的编码转换问题(解决乱码问题)

    在JSP中,为了避免乱码,需要设置页面编码和请求编码。在JSP头部使用`;charset=UTF-8" %&gt;`设定页面编码,同时确保HTML `&lt;meta&gt;`标签中也指定了正确的字符集。对于请求参数,可以通过`request.setCharacterEncoding(...

    常见编码及乱码的处理.pdf

    编码、字符、字符集和乱码处理的知识点概述: 1. 编码和字符: 编码是指用一系列规则来表示信息的方式,这些规则指定了如何将信息转换成计算机可以处理的形式。字符是构成文字和符号的基本单位,包括字母、数字、...

    解决rf中文乱码问题

    总的来说,解决RF中文乱码问题需要从多个角度进行排查和调整。从测试数据的编码、RF的日志和报告设置,到与外部系统的交互和库的兼容性,每个环节都可能成为关键。通过细心的检查和配置,你应该能够成功地消除乱码,...

    解决中文乱码问题

    标签中提到的是中文乱码和C#语言。中文乱码是指在计算机中,中文字符由于编码问题而导致的乱码问题。C#是微软公司推出的一个面向对象的编程语言,广泛应用于Windows平台的软件开发中。 部分内容解释 部分内容中...

    解决中文乱码问题专题

    在计算机领域,中文乱码是一个常见的...对于初学者而言,掌握这些知识将有助于避免或快速解决可能出现的乱码问题,提升工作效率。在实际操作中,养成良好的编码习惯,使用标准的字符集,将大大降低乱码问题的发生概率。

    日文乱码转换工具

    乱码通常出现在计算机系统不支持或识别错误的字符编码格式时,导致文本无法正常显示。日文乱码尤其常见,因为日语使用了多种字符集,包括平假名、片假名和汉字,这些字符在不同的编码标准(如Shift-JIS、EUC-JP、UTF...

    python抓取并保存html页面时乱码问题的解决方法

    综上所述,解决Python抓取HTML页面时的乱码问题需要正确识别和处理编码。通过检测网页的编码、比较声明编码以及在必要时转换编码,可以有效地避免乱码的出现。此外,使用适当的库和模块(如`chardet`和`...

    计算机编码知识,VB介绍

    计算机编码知识,尤其是字符编码,是计算机科学中的基础概念,它涉及到如何在计算机内部表示和处理文本。VB6.0,即Visual Basic 6.0,是微软开发的一种面向对象的编程语言,它同样需要理解字符编码以便正确处理文本...

    eclipse导出程序出现乱码解决方法

    解决Eclipse导出程序时出现乱码问题的关键在于理解编码的层次和一致性。从项目到文件,再到运行环境,每一步都需要仔细检查和调整。通过上述步骤,大多数乱码问题都能得到解决。 在这个过程中,了解和掌握不同编码...

    解决连接mysql中文显示乱码

    ### 解决中文显示乱码的关键知识点 #### 1. **理解字符集和编码** 字符集(Character Set)是指用于表示文字的一系列符号集合,而编码(Encoding)则是将这些符号转换为计算机能够识别的二进制数据的过程。例如,UTF-8...

    java字符集编码乱码详解

    解决乱码的关键在于确保编码的一致性和正确性。具体做法包括: 1. **确定源文件的编码**: 在 Java 程序中处理文件时,明确指定文件的编码方式。 2. **设置系统默认编码**: 在 Java 应用程序启动时,可以通过 `-...

    Java Web程序开发中字符乱码的原因与解决办法.pdf

    为了解决这个问题,我们首先要了解字符集和字符编码的相关知识,然后分析字符乱码的原因,并最终找出相应的解决办法。 字符集是字符的集合,它包含了计算机能够处理的文本数据。常见的字符集有ASCII字符集、Unicode...

    一次性解决中文乱码问题

    乱码通常发生在计算机系统处理字符编码不一致的情况下,即数据的输入、存储和显示使用了不同的字符集。中文字符集主要有GBK、GB2312、UTF-8等,如果它们之间不匹配,就会导致字符无法正确显示,出现乱码现象。 1. *...

    计算机编码与Unicode(PPT课件)

    计算机编码是计算机科学中的基础概念,它涉及到如何在二进制的世界中表示和处理人类语言。...通过学习这些知识,开发者和IT专业人员能够更好地处理和解决涉及多语言的编码问题,提升软件的兼容性和国际化程度。

    java 编码,乱码,字符系列(1)

    在IT行业中,编程语言是构建软件的基础,而Java作为一款...在实际工作中,我们需要时刻警惕可能出现的乱码问题,并采取有效措施进行预防和解决。通过深入学习和实践,我们可以更好地驾驭字符编码,避免不必要的麻烦。

    php页面乱码解决

    为了防止未来的乱码问题,建议统一所有文件和数据库的字符集编码。新建项目时,从一开始就采用UTF-8编码,这是最广泛支持的编码方式,能有效减少乱码问题的发生。 通过以上步骤,我们可以有效地解决PHP页面乱码问题...

    CodeView v2.22[乱码查看器]

    CodeView v2.22是一款专门用于处理乱码问题的查看器,它为程序员和普通用户提供了一种方便的方式来查看和解决编码错误导致的乱码问题。在计算机领域,乱码通常发生在不同编码格式不匹配或者数据传输过程中编码转换...

Global site tag (gtag.js) - Google Analytics