﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-KE-文章分类-encoding</title><link>http://www.blogjava.net/keweibo/category/25848.html</link><description>As long as you are there to lead me ,I won't lose my way </description><language>zh-cn</language><lastBuildDate>Fri, 21 Sep 2007 10:11:29 GMT</lastBuildDate><pubDate>Fri, 21 Sep 2007 10:11:29 GMT</pubDate><ttl>60</ttl><item><title>中文乱码问题产生的由来</title><link>http://www.blogjava.net/keweibo/articles/146931.html</link><dc:creator>KE</dc:creator><author>KE</author><pubDate>Thu, 20 Sep 2007 13:46:00 GMT</pubDate><guid>http://www.blogjava.net/keweibo/articles/146931.html</guid><wfw:comment>http://www.blogjava.net/keweibo/comments/146931.html</wfw:comment><comments>http://www.blogjava.net/keweibo/articles/146931.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/keweibo/comments/commentRss/146931.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/keweibo/services/trackbacks/146931.html</trackback:ping><description><![CDATA[<p>中文乱码问题产生的由来</p>
<p>&nbsp;在计算机中，只有二进制的数据，不管数据是在内存中，还是在外部设备上。<br />
对于我们所看到的字符，也是以二进制数据的形式存在的。不同的字符对应的<br />
二进制数的规则，就是字符的编码。字符编码的集合称为字符集。</p>
<p>常用的字符集<br />
1 ASCII<br />
2 ISO8859-1<br />
3 GB2312和GBK</p>
<p>&nbsp;&nbsp;每个国家（或者区域）都规定了计算机信息交换用的字符编码集，这就造成了<br />
交流上的困难。想象一下，你发送一封中文邮件给一位远在西班牙的朋友，当邮件<br />
通过网络发送出去的时候，你所书写的中文字符会按照本地的字符集GBK转换成二进制<br />
编码数据，然后发送出去。当你的朋友接收以邮件（二进制数据）后，查看信件时，会<br />
按照他所用的系统的字符集，将二进制数据解码为字符，然而由于两种字符集之间编码<br />
规则的不同，导致转换出现乱码。这是因为，在不同的字符集之间，同样的数字可能对<br />
应了不同的符号，也可能在另一种字符集中，该数字没也对应的符号。<br />
&nbsp;&nbsp;为解决上述问题，统一全世界的字符编码，由Unicode协会制定并发布了Unicode编码。<br />
4 Unicode<br />
5 UTF-8</p>
<p>对乱码产生过程的分析</p>
<p>&nbsp;&nbsp;字符数据在各种不同的字符集之间转换时，就有可能会出现乱码。<br />
&nbsp;&nbsp;xml处理器在没有被预先通知的情况下会默认文档数据为UTF-8格式。<br />
这样在你书写xml文档时，就存在了UTF-8字符集和本地字符进行转换的过程。<br />
当向xml文档中写入数据的时候，需要将本地字符集编码的数据转换为UTF-8<br />
，而在输出字符数据的时候，则需要将UTF-8编码转换为本地字符集编码。<br />
从上述的过程来看，写入和输出的过程是可逆的。理应不会出现中文乱码问题<br />
然而，实际应用的情形，比上述的过程要复杂的多。在WEB应用中，通过都包括<br />
浏览器、WEB服务器、WEB应用程序和数据库等部分，每一部分的都有可能使用不<br />
同的字符集，从而导致数据在各种字符之间转换时，出现了乱码问题。</p>
<img src ="http://www.blogjava.net/keweibo/aggbug/146931.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/keweibo/" target="_blank">KE</a> 2007-09-20 21:46 <a href="http://www.blogjava.net/keweibo/articles/146931.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>几种误解，以及乱码产生的原因和解决办法</title><link>http://www.blogjava.net/keweibo/articles/144979.html</link><dc:creator>KE</dc:creator><author>KE</author><pubDate>Thu, 13 Sep 2007 14:34:00 GMT</pubDate><guid>http://www.blogjava.net/keweibo/articles/144979.html</guid><wfw:comment>http://www.blogjava.net/keweibo/comments/144979.html</wfw:comment><comments>http://www.blogjava.net/keweibo/articles/144979.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/keweibo/comments/commentRss/144979.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/keweibo/services/trackbacks/144979.html</trackback:ping><description><![CDATA[几种误解，以及乱码产生的原因和解决办法
<h5>3.1 容易产生的误解</h5>
<table cellspacing="0" cellpadding="3" border="0">
    <tbody>
        <tr>
            <td class="top_1">　</td>
            <td class="top_2" align="center"><strong>对编码的误解</strong></td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">误解一</td>
            <td class="con_2">在将&#8220;字节串&#8221;转化成&#8220;UNICODE 字符串&#8221;时，比如在读取文本文件时，或者通过网络传输文本时，容易将&#8220;字节串&#8221;简单地作为<strong>单字节字符串</strong>，采用每&#8220;一个字节&#8221;就是&#8220;一个字符&#8221;的方法进行转化。<br />
            <br />
            而实际上，在非英文的环境中，应该将&#8220;字节串&#8221;作为 ANSI 字符串，采用适当的编码来得到 UNICODE 字符串，有可能&#8220;多个字节&#8221;才能得到&#8220;一个字符&#8221;。<br />
            <br />
            通常，一直在英文环境下做开发的程序员们，容易有这种误解。</td>
        </tr>
        <tr>
            <td class="bot_1" noWrap align="center">误解二</td>
            <td class="bot_2">在 DOS，Windows 98 等非 UNICODE 环境下，字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串，必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维：&#8220;字符串的编码&#8221;。<br />
            <br />
            当 UNICODE 被支持后，Java 中的 String 是以字符的&#8220;序号&#8221;来存储的，不是以&#8220;某种编码的字节&#8221;来存储的，因此已经不存在&#8220;字符串的编码&#8221;这个概念了。只有在&#8220;字符串&#8221;与&#8220;字节串&#8221;转化时，或者，将一个&#8220;字节串&#8221;当成一个 ANSI 字符串时，才有编码的概念。<br />
            <br />
            不少的人都有这个误解。</td>
        </tr>
    </tbody>
</table>
<p>第一种误解，往往是导致乱码产生的原因。第二种误解，往往导致本来容易纠正的乱码问题变得更复杂。</p>
<p>在这里，我们可以看到，其中所讲的&#8220;误解一&#8221;，即采用每&#8220;一个字节&#8221;就是&#8220;一个字符&#8221;的转化方法，实际上也就等同于采用 iso-8859-1 进行转化。因此，我们常常使用 bytes = string.getBytes("iso-8859-1") 来进行逆向操作，得到原始的&#8220;字节串&#8221;。然后再使用正确的 ANSI 编码，比如 string = new String(bytes, "GB2312")，来得到正确的&#8220;UNICODE 字符串&#8221;。</p>
<h5><a id="instances" name="instances"></a>3.2 非 UNICODE 程序在不同语言环境间移植时的乱码</h5>
<p>非 UNICODE 程序中的字符串，都是以某种 ANSI 编码形式存在的。如果程序运行时的语言环境与开发时的语言环境不同，将会导致 ANSI 字符串的显示失败。</p>
<p>比如，在日文环境下开发的非 UNICODE 的日文程序界面，拿到中文环境下运行时，界面上将显示乱码。如果这个日文程序界面改为采用 UNICODE 来记录字符串，那么当在中文环境下运行时，界面上将可以显示正常的日文。</p>
<p>由于客观原因，有时候我们必须在中文操作系统下运行非 UNICODE 的日文软件，这时我们可以采用一些工具，比如，南极星，AppLocale 等，暂时的模拟不同的语言环境。</p>
<h5>3.3 网页提交字符串</h5>
<p>当页面中的表单提交字符串时，首先把字符串按照当前页面的编码，转化成字节串。然后再将每个字节转化成 "%XX" 的格式提交到 Web 服务器。比如，一个编码为 GB2312 的页面，提交 "中" 这个字符串时，提交给服务器的内容为 "%D6%D0"。</p>
<p>在服务器端，Web 服务器把收到的 "%D6%D0" 转化成 [0xD6, 0xD0] 两个字节，然后再根据 GB2312 编码规则得到 "中" 字。</p>
<p>在 Tomcat 服务器中，request.getParameter() 得到乱码时，常常是因为前面提到的&#8220;误解一&#8221;造成的。默认情况下，当提交 "%D6%D0" 给 Tomcat 服务器时，request.getParameter() 将返回 [0x00D6, 0x00D0] 两个 UNICODE 字符，而不是返回一个 "中" 字符。因此，我们需要使用 bytes = string.getBytes("iso-8859-1") 得到原始的字节串，再用 string = new String(bytes, "GB2312") 重新得到正确的字符串 "中"。</p>
<h5>3.4 从数据库读取字符串</h5>
<p>通过数据库客户端（比如 ODBC 或 JDBC）从数据库服务器中读取字符串时，客户端需要从服务器获知所使用的 ANSI 编码。当数据库服务器发送字节流给客户端时，客户端负责将字节流按照正确的编码转化成 UNICODE 字符串。</p>
<p>如果从数据库读取字符串时得到乱码，而数据库中存放的数据又是正确的，那么往往还是因为前面提到的&#8220;误解一&#8221;造成的。解决的办法还是通过 string = new String( string.getBytes("iso-8859-1"), "GB2312") 的方法，重新得到原始的字节串，再重新使用正确的编码转化成字符串。</p>
<h5>3.5 电子邮件中的字符串</h5>
<p>当一段 Text 或者 HTML 通过电子邮件传送时，发送的内容首先通过一种指定的<strong>字符编码</strong>转化成&#8220;字节串&#8221;，然后再把&#8220;字节串&#8221;通过一种指定的<strong>传输编码</strong>（Content-Transfer-Encoding）进行转化得到另一串&#8220;字节串&#8221;。比如，打开一封电子邮件源代码，可以看到类似的内容：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code">Content-Type: text/plain;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#ff0000">charset="gb2312"</font><br />
            <font color="#ff0000">Content-Transfer-Encoding: base64</font><br />
            <br />
            sbG+qcrQuqO17cf4yee74bGjz9W7+b3wudzA7dbQ0MQNCg0KvPKzxqO6uqO17cnnsaPW0NDEDQoNCg==</td>
        </tr>
    </tbody>
</table>
<p>最常用的 Content-Transfer-Encoding 有 Base64 和 Quoted-Printable 两种。在对二进制文件或者中文文本进行转化时，Base64 得到的&#8220;字节串&#8221;比 Quoted-Printable 更短。在对英文文本进行转化时，Quoted-Printable 得到的&#8220;字节串&#8221;比 Base64 更短。</p>
<p>邮件的标题，用了一种更简短的格式来标注&#8220;字符编码&#8221;和&#8220;传输编码&#8221;。比如，标题内容为 "中"，则在邮件源代码中表示为：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 正确的标题格式</span><br />
            Subject: <span style="background-color: #ffff00">=?</span>GB2312<span style="background-color: #ffff00">?B?</span>1tA=<span style="background-color: #ffff00">?=</span></td>
        </tr>
    </tbody>
</table>
<p>其中，</p>
<ul>
    <li>第一个&#8220;=?&#8221;与&#8220;?&#8221;中间的部分指定了字符编码，在这个例子中指定的是 GB2312。
    <li>&#8220;?&#8221;与&#8220;?&#8221;中间的&#8220;B&#8221;代表 Base64。如果是&#8220;Q&#8221;则代表 Quoted-Printable。
    <li>最后&#8220;?&#8221;与&#8220;?=&#8221;之间的部分，就是经过 GB2312 转化成字节串，再经过 Base64 转化后的标题内容。 </li>
</ul>
<p>如果&#8220;传输编码&#8221;改为 Quoted-Printable，同样，如果标题内容为 "中"：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 正确的标题格式</span><br />
            Subject: <span style="background-color: #ffff00">=?</span>GB2312<span style="background-color: #ffff00">?Q?</span>=D6=D0<span style="background-color: #ffff00">?=</span></td>
        </tr>
    </tbody>
</table>
<p>如果阅读邮件时出现乱码，一般是因为&#8220;字符编码&#8221;或&#8220;传输编码&#8221;指定有误，或者是没有指定。比如，有的发邮件组件在发送邮件时，标题 "中"：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 错误的标题格式</span><br />
            Subject: <span style="background-color: #ffff00">=?</span><font color="#ff0000">ISO-8859-1</font><span style="background-color: #ffff00">?Q?</span>=D6=D0<span style="background-color: #ffff00">?=</span></td>
        </tr>
    </tbody>
</table>
<p>这样的表示，实际上是明确指明了标题为 [0x00D6, 0x00D0]，即 "中"，而不是 "中"。</p>
<h4><a id="correct" name="correct"></a>4. 几种错误理解的纠正</h4>
<h5>误解：&#8220;ISO-8859-1 是国际编码？&#8221;</h5>
<p>非也。iso-8859-1 只是单字节字符集中最简单的一种，也就是&#8220;字节编号&#8221;与&#8220;UNICODE 字符编号&#8221;一致的那种编码规则。当我们要把一个&#8220;字节串&#8221;转化成&#8220;字符串&#8221;，而又不知道它是哪一种 ANSI 编码时，先暂时地把&#8220;每一个字节&#8221;作为&#8220;一个字符&#8221;进行转化，不会造成信息丢失。然后再使用 bytes = string.getBytes("iso-8859-1") 的方法可恢复到原始的字节串。</p>
<h5>误解：&#8220;Java 中，怎样知道某个字符串的内码？&#8221;</h5>
<p>Java 中，字符串类 java.lang.String 处理的是 UNICODE 字符串，不是 ANSI 字符串。我们只需要把字符串作为&#8220;抽象的符号的串&#8221;来看待。因此不存在字符串的内码的问题。</p>
<div class="invisible" id="reference">文章引用自：<a href="" target=" href_cetemp="  ?></a> </div>
  <img src ="http://www.blogjava.net/keweibo/aggbug/144979.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/keweibo/" target="_blank">KE</a> 2007-09-13 22:34 <a href="http://www.blogjava.net/keweibo/articles/144979.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符，字节和编码</title><link>http://www.blogjava.net/keweibo/articles/144978.html</link><dc:creator>KE</dc:creator><author>KE</author><pubDate>Thu, 13 Sep 2007 14:25:00 GMT</pubDate><guid>http://www.blogjava.net/keweibo/articles/144978.html</guid><wfw:comment>http://www.blogjava.net/keweibo/comments/144978.html</wfw:comment><comments>http://www.blogjava.net/keweibo/articles/144978.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/keweibo/comments/commentRss/144978.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/keweibo/services/trackbacks/144978.html</trackback:ping><description><![CDATA[<h2><a name="main"></a>字符，字节和编码</h2>
<p><font size="1">[原创文章，转载请保留或注明出处：<a href="http://www.regexlab.com/zh/encoding.htm">http://www.regexlab.com/zh/encoding.htm</a>]</font></p>
<p>级别：中级</p>
<blockquote>
<p>摘要：本文介绍了字符与编码的发展过程，相关概念的正确理解。举例说明了一些实际应用中，编码的实现方法。然后，本文讲述了通常对字符与编码的几种误解，由于这些误解而导致乱码产生的原因，以及消除乱码的办法。本文的内容涵盖了&#8220;中文问题&#8221;，&#8220;乱码问题&#8221;。</p>
<p>掌握编码问题的关键是正确地理解相关概念，编码所涉及的技术其实是很简单的。因此，阅读本文时需要慢读多想，多思考。</p>
</blockquote>
<h4><a name="intro"></a>引言</h4>
<p>&#8220;字符与编码&#8221;是一个被经常讨论的话题。即使这样，时常出现的乱码仍然困扰着大家。虽然我们有很多的办法可以用来消除乱码，但我们并不一定理解这些办法的内在原理。而有的乱码产生的原因，实际上由于底层代码本身有问题所导致的。因此，不仅是初学者会对字符编码感到模糊，有的底层开发人员同样对字符编码缺乏准确的理解。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h4><a name="develop"></a>1. 编码问题的由来，相关概念的理解</h4>
<h5>1.1 字符与编码的发展</h5>
<p>从计算机对多国语言的支持角度看，大致可以分为三个阶段：</p>
<table cellspacing="0" cellpadding="3" border="0">
    <tbody>
        <tr>
            <td class="top_1">　</td>
            <td class="top_2" noWrap align="center"><strong>系统内码</strong></td>
            <td class="top_2" align="center"><strong>说明</strong></td>
            <td class="top_2" align="center"><strong>系统</strong></td>
        </tr>
        <tr>
            <td class="con_1" noWrap>阶段一</td>
            <td class="con_2" noWrap align="center">ASCII</td>
            <td class="con_2">计算机刚开始只支持英语，其它语言不能够在计算机上存储和显示。</td>
            <td class="con_2">英文 DOS</td>
        </tr>
        <tr>
            <td class="con_1" noWrap>阶段二</td>
            <td class="con_2" noWrap align="center">ANSI编码<br />
            （本地化）</td>
            <td class="con_2">为使计算机支持更多语言，通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如：汉字 '中' 在中文操作系统中，使用 [0xD6,0xD0] 这两个字节存储。<br />
            <br />
            不同的国家和地区制定了不同的标准，由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式，称为<strong> ANSI 编码</strong>。在简体中文系统下，ANSI 编码代表 GB2312 编码，在日文操作系统下，ANSI 编码代表 JIS 编码。<br />
            <br />
            不同 ANSI 编码之间互不兼容，当信息在国际间交流时，无法将属于两种语言的文字，存储在同一段<strong> ANSI 编码</strong>的文本中。</td>
            <td class="con_2">中文 DOS，中文 Windows 95/98，日文 Windows 95/98</td>
        </tr>
        <tr>
            <td class="bot_1" noWrap>阶段三</td>
            <td class="bot_2" noWrap align="center">UNICODE<br />
            （国际化）</td>
            <td class="bot_2">为了使国际间信息交流更加方便，国际组织制定了 <strong>UNICODE 字符集</strong>，为各种语言中的每一个字符设定了统一并且唯一的数字编号，以满足跨语言、跨平台进行文本转换、处理的要求。</td>
            <td class="bot_2">Windows NT/2000/XP，Linux，Java</td>
        </tr>
    </tbody>
</table>
<p>字符串在内存中的存放方法：</p>
<p>在 ASCII 阶段，<strong>单字节字符串</strong>使用一个字节存放一个字符（SBCS）。比如，"Bob123" 在内存中为：</p>
<table style="font-size: 80%; color: #000080" cellspacing="5" cellpadding="0" border="0">
    <tbody>
        <tr>
            <td>42</td>
            <td>6F</td>
            <td>62</td>
            <td>31</td>
            <td>32</td>
            <td>33</td>
            <td>00</td>
        </tr>
        <tr>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
        </tr>
        <tr>
            <td align="center">B</td>
            <td align="center">o</td>
            <td align="center">b</td>
            <td align="center">1</td>
            <td align="center">2</td>
            <td align="center">3</td>
            <td align="center">\0</td>
        </tr>
    </tbody>
</table>
<p>在使用 ANSI 编码支持多种语言阶段，每个字符使用一个字节或多个字节来表示（MBCS），因此，这种方式存放的字符也被称作<strong>多字节字符</strong>。比如，"中文123" 在中文 Windows 95 内存中为7个字节，每个汉字占2个字节，每个英文和数字字符占1个字节：</p>
<table style="font-size: 80%; color: #000080" cellspacing="5" cellpadding="0" border="0">
    <tbody>
        <tr>
            <td>D6</td>
            <td>D0</td>
            <td>CE</td>
            <td>C4</td>
            <td>31</td>
            <td>32</td>
            <td>33</td>
            <td>00</td>
        </tr>
        <tr>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
        </tr>
        <tr>
            <td align="center" colspan="2">中</td>
            <td align="center" colspan="2">文</td>
            <td align="center">1</td>
            <td align="center">2</td>
            <td align="center">3</td>
            <td align="center">\0</td>
        </tr>
    </tbody>
</table>
<p>在 UNICODE 被采用之后，计算机存放字符串时，改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节（16 位）来存放一个序号（DBCS），因此，这种方式存放的字符也被称作<strong>宽字节字符</strong>。比如，字符串 "中文123" 在 Windows 2000 下，内存中实际存放的是 5 个序号：</p>
<table style="font-size: 80%; color: #000080" cellspacing="5" cellpadding="0" border="0">
    <tbody>
        <tr>
            <td valign="bottom">2D</td>
            <td valign="bottom">4E</td>
            <td valign="bottom">87</td>
            <td valign="bottom">65</td>
            <td valign="bottom">31</td>
            <td valign="bottom">00</td>
            <td valign="bottom">32</td>
            <td valign="bottom">00</td>
            <td valign="bottom">33</td>
            <td valign="bottom">00</td>
            <td valign="bottom">00</td>
            <td valign="bottom">00</td>
            <td><font color="#808080">&nbsp;&nbsp;&nbsp;&nbsp; &#8592; 在 x86 CPU 中，低字节在前</font></td>
        </tr>
        <tr>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
            <td><img height="1" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0"  alt="" /></td>
        </tr>
        <tr>
            <td align="center" colspan="2">中</td>
            <td align="center" colspan="2">文</td>
            <td align="center" colspan="2">1</td>
            <td align="center" colspan="2">2</td>
            <td align="center" colspan="2">3</td>
            <td align="center" colspan="2">\0</td>
            <td align="center">　</td>
        </tr>
    </tbody>
</table>
<p>一共占 10 个字节。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h5><a name="concept"></a>1.2 字符，字节，字符串</h5>
<p>理解编码的关键，是要把字符的概念和字节的概念理解准确。这两个概念容易混淆，我们在此做一下区分：</p>
<table cellspacing="0" cellpadding="3" border="0">
    <tbody>
        <tr>
            <td class="top_1">　</td>
            <td class="top_2" align="center"><strong>概念描述</strong></td>
            <td class="top_2" align="center"><strong>举例</strong></td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">字符</td>
            <td class="con_2">人们使用的记号，抽象意义上的一个符号。</td>
            <td class="con_2">'1', '中', 'a', '$', '￥', &#8230;&#8230;</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">字节</td>
            <td class="con_2">计算机中存储数据的单元，一个8位的二进制数，是一个很具体的存储空间。</td>
            <td class="con_2">0x01, 0x45, 0xFA, &#8230;&#8230;</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">ANSI<br />
            字符串</td>
            <td class="con_2">在内存中，如果&#8220;字符&#8221;是以 <strong>ANSI 编码</strong>形式存在的，一个字符可能使用一个字节或多个字节来表示，那么我们称这种字符串为 <strong>ANSI 字符串</strong>或者<strong>多字节字符串</strong>。</td>
            <td class="con_2">"中文123"<br />
            <span class="rem">（占7字节）</span></td>
        </tr>
        <tr>
            <td class="bot_1" noWrap align="center">UNICODE<br />
            字符串</td>
            <td class="bot_2">在内存中，如果&#8220;字符&#8221;是以在 UNICODE 中的序号存在的，那么我们称这种字符串为 <strong>UNICODE 字符串</strong>或者<strong>宽字节字符串</strong>。</td>
            <td class="bot_2">L"中文123"<br />
            <span class="rem">（占10字节）</span></td>
        </tr>
    </tbody>
</table>
<p>由于不同 ANSI 编码所规定的标准是不相同的，因此，对于一个给定的<strong>多字节字符串</strong>，我们必须知道它采用的是哪一种编码规则，才能够知道它包含了哪些&#8220;字符&#8221;。而对于 <strong>UNICODE 字符串</strong>来说，不管在什么环境下，它所代表的&#8220;字符&#8221;内容总是不变的。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h5>1.3 字符集与编码</h5>
<p>各个国家和地区所制定的不同 ANSI 编码标准中，都只规定了各自语言所需的&#8220;字符&#8221;。比如：汉字标准（GB2312）中没有规定韩国语字符怎样存储。这些 ANSI 编码标准所规定的内容包含两层含义：</p>
<ol>
    <li>使用哪些字符。也就是说哪些汉字，字母和符号会被收入标准中。所包含&#8220;字符&#8221;的集合就叫做&#8220;<strong>字符集</strong>&#8221;。
    <li>规定每个&#8220;字符&#8221;分别用一个字节还是多个字节存储，用哪些字节来存储，这个规定就叫做&#8220;<strong>编码</strong>&#8221;。 </li>
</ol>
<p>各个国家和地区在制定编码标准的时候，&#8220;字符的集合&#8221;和&#8220;编码&#8221;一般都是同时制定的。因此，平常我们所说的&#8220;字符集&#8221;，比如：GB2312, GBK, JIS 等，除了有&#8220;字符的集合&#8221;这层含义外，同时也包含了&#8220;编码&#8221;的含义。</p>
<p>&#8220;<strong>UNICODE 字符集</strong>&#8221;包含了各种语言中使用到的所有&#8220;字符&#8221;。用来给 UNICODE 字符集编码的标准有很多种，比如：UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h5>1.4 常用的编码简介</h5>
<p>简单介绍一下常用的编码规则，为后边的章节做一个准备。在这里，我们根据编码规则的特点，把所有的编码分成三类：</p>
<table cellspacing="0" cellpadding="3" border="0">
    <tbody>
        <tr>
            <td class="top_1" align="center"><strong>分类</strong></td>
            <td class="top_2" align="center"><strong>编码标准</strong></td>
            <td class="top_2" align="center"><strong>说明</strong></td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">单字节字符编码</td>
            <td class="con_2">ISO-8859-1</td>
            <td class="con_2">最简单的编码规则，每一个字节直接作为一个 UNICODE 字符。比如，[0xD6, 0xD0] 这两个字节，通过 iso-8859-1 转化为字符串时，将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符，即 "&#214;&#208;"。<br />
            <br />
            反之，将 UNICODE 字符串通过 iso-8859-1 转化为字节串时，只能正常转化 0~255 范围的字符。</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">ANSI 编码</td>
            <td class="con_2">GB2312,<br />
            BIG5,<br />
            Shift_JIS,<br />
            ISO-8859-2 &#8230;&#8230;</td>
            <td class="con_2">把 UNICODE 字符串通过 ANSI 编码转化为&#8220;字节串&#8221;时，根据各自编码的规定，一个 UNICODE 字符可能转化成一个字节或多个字节。<br />
            <br />
            反之，将字节串转化成字符串时，也可能多个字节转化成一个字符。比如，[0xD6, 0xD0] 这两个字节，通过 GB2312 转化为字符串时，将得到 [0x4E2D] 一个字符，即 '中' 字。<br />
            <br />
            &#8220;ANSI 编码&#8221;的特点：<br />
            1. 这些&#8220;ANSI 编码标准&#8221;都只能处理各自语言范围之内的 UNICODE 字符。<br />
            2. &#8220;UNICODE 字符&#8221;与&#8220;转换出来的字节&#8221;之间的关系是人为规定的。</td>
        </tr>
        <tr>
            <td class="bot_1" noWrap align="center">UNICODE 编码</td>
            <td class="bot_2">UTF-8,<br />
            UTF-16, UnicodeBig &#8230;&#8230;</td>
            <td class="bot_2">与&#8220;ANSI 编码&#8221;类似的，把字符串通过 UNICODE 编码转化成&#8220;字节串&#8221;时，一个 UNICODE 字符可能转化成一个字节或多个字节。<br />
            <br />
            与&#8220;ANSI 编码&#8221;不同的是：<br />
            1. 这些&#8220;UNICODE 编码&#8221;能够处理所有的 UNICODE 字符。<br />
            2. &#8220;UNICODE 字符&#8221;与&#8220;转换出来的字节&#8221;之间是可以通过计算得到的。</td>
        </tr>
    </tbody>
</table>
<p>我们实际上没有必要去深究每一种编码具体把某一个字符编码成了哪几个字节，我们只需要知道&#8220;编码&#8221;的概念就是把&#8220;字符&#8221;转化成&#8220;字节&#8221;就可以了。对于&#8220;UNICODE 编码&#8221;，由于它们是可以通过计算得到的，因此，在特殊的场合，我们可以去了解某一种&#8220;UNICODE 编码&#8221;是怎样的规则。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h4><a name="implement"></a>2. 字符与编码在程序中的实现</h4>
<h5>2.1 程序中的字符与字节</h5>
<p>在 C++ 和 Java 中，用来代表&#8220;字符&#8221;和&#8220;字节&#8221;的数据类型，以及进行编码的方法：</p>
<table cellspacing="0" cellpadding="3" border="0">
    <tbody>
        <tr>
            <td class="top_1" align="center"><strong>类型或操作</strong></td>
            <td class="top_2" align="center"><strong>C++</strong></td>
            <td class="top_2" align="center"><strong>Java</strong></td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">字符</td>
            <td class="con_2">wchar_t</td>
            <td class="con_2">char</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">字节</td>
            <td class="con_2">char</td>
            <td class="con_2">byte</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">ANSI 字符串</td>
            <td class="con_2">char[]</td>
            <td class="con_2">byte[]</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">UNICODE 字符串</td>
            <td class="con_2">wchar_t[]</td>
            <td class="con_2">String</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">字节串&#8594;字符串</td>
            <td class="con_2">mbstowcs(), MultiByteToWideChar()</td>
            <td class="con_2">string = new String(bytes, "encoding")</td>
        </tr>
        <tr>
            <td class="bot_1" noWrap align="center">字符串&#8594;字节串</td>
            <td class="bot_2">wcstombs(), WideCharToMultiByte()</td>
            <td class="bot_2">bytes = string.getBytes("encoding")</td>
        </tr>
    </tbody>
</table>
<p>以上需要注意几点：</p>
<ol>
    <li>Java 中的 char 代表一个&#8220;UNICODE 字符（宽字节字符）&#8221;，而 C++ 中的 char 代表一个字节。
    <li>MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函数。 </li>
</ol>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h5>2.2 C++ 中相关实现方法</h5>
<p>声明一段字符串常量：</p>
<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// ANSI 字符串，内容长度 7 字节</span><span class="key"><br />
            char</span>&nbsp;&nbsp;&nbsp;&nbsp; sz[<span class="number">20</span>] = <span class="string">"中文123"</span>;<br />
            <br />
            <span class="rem">// UNICODE 字符串，内容长度 5 个 wchar_t（10 字节）</span><br />
            wchar_t wsz[<span class="number">20</span>] = L<span class="string">"\x4E2D\x6587\x0031\x0032\x0033"</span>;</td>
        </tr>
    </tbody>
</table>
<p>UNICODE 字符串的 I/O 操作，字符与字节的转换操作：</p>
<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 运行时设定当前 ANSI 编码，VC 格式<br />
            </span>setlocale(LC_ALL, <span class="string">".936"</span>);<br />
            <br />
            <span class="rem">// GCC 中格式</span><br />
            setlocale(LC_ALL, <span class="string">"zh_CN.GBK"</span>);<br />
            <br />
            <span class="rem">// Visual C++ 中使用小写 %s，按照 setlocale 指定编码输出到文件<br />
            // GCC 中使用大写 %S</span><br />
            fwprintf(fp, L<span class="string">"%s\n"</span>, wsz);<br />
            <br />
            <span class="rem">// 把 UNICODE 字符串按照 setlocale 指定的编码转换成字节</span><br />
            wcstombs(sz, wsz, <span class="number">20</span>);<span class="rem"><br />
            // 把字节串按照 setlocale 指定的编码转换成 UNICODE 字符串<br />
            </span>mbstowcs(wsz, sz, <span class="number">20</span>);</td>
        </tr>
    </tbody>
</table>
<p>在 Visual C++ 中，UNICODE 字符串常量有更简单的表示方法。如果源程序的编码与当前默认 ANSI 编码不符，则需要使用 #pragma setlocale，告诉编译器源程序使用的编码：</p>
<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 如果源程序的编码与当前默认 ANSI 编码不一致，<br />
            // 则需要此行，编译时用来指明当前源程序使用的编码</span><span class="key"><br />
            #pragma setlocale</span>(<span class="string">".936"</span>)<br />
            <br />
            <span class="rem">// UNICODE 字符串常量，内容长度 10 字节</span><br />
            wchar_t wsz[<span class="number">20</span>] = L<span class="string">"中文123"</span>;</td>
        </tr>
    </tbody>
</table>
<p>以上需要注意 #pragma setlocale 与 setlocale(LC_ALL, "") 的作用是不同的，#pragma setlocale 在编译时起作用，setlocale() 在运行时起作用。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h5>2.3 Java 中相关实现方法</h5>
<p>字符串类 String 中的内容是 UNICODE 字符串：</p>
<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// Java 代码，直接写中文</span><span class="pw"><br />
            String</span> string = <span class="string">"中文123"</span>;<br />
            <br />
            <span class="rem">// 得到长度为 5，因为是 5 个字符</span><br />
            <span class="pw">System</span>.out.println(string.length());</td>
        </tr>
    </tbody>
</table>
<p>字符串 I/O 操作，字符与字节转换操作。在 Java 包 java.io.* 中，以&#8220;Stream&#8221;结尾的类一般是用来操作&#8220;字节串&#8221;的类，以&#8220;Reader&#8221;，&#8220;Writer&#8221;结尾的类一般是用来操作&#8220;字符串&#8221;的类。</p>
<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 字符串与字节串间相互转化<br />
            <br />
            // 按照 GB2312 得到字节（得到多字节字符串）</span><span class="key"><br />
            byte</span> [] bytes = string.getBytes(<span class="string">"GB2312"</span>);<br />
            <br />
            <span class="rem">// 从字节按照 GB2312 得到 UNICODE 字符串</span><br />
            string = <span class="key">new</span> <span class="pw">String</span>(bytes, <span class="string">"GB2312"</span>);<br />
            <br />
            <span class="rem">// 要将 String 按照某种编码写入文本文件，有两种方法：<br />
            <br />
            // 第一种办法：用 Stream 类写入已经按照指定编码转化好的字节串</span><br />
            OutputStream os = <span class="key">new</span> FileOutputStream(<span class="string">"1.txt"</span>);<br />
            os.write(bytes);<br />
            os.close();<br />
            <br />
            <span class="rem">// 第二种办法：构造指定编码的 Writer 来写入字符串</span><br />
            Writer ow = <span class="key">new</span> OutputStreamWriter(<span class="key">new</span> FileOutputStream(<span class="string">"2.txt"</span>), <span class="string">"GB2312"</span>);<br />
            ow.write(string);<br />
            ow.close();<br />
            <br />
            <span class="rem">/* 最后得到的 1.txt 和 2.txt 都是 7 个字节 */</span></td>
        </tr>
    </tbody>
</table>
<p>如果 java 的源程序编码与当前默认 ANSI 编码不符，则在编译的时候，需要指明一下源程序的编码。比如：</p>
<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code">E:\&gt;javac <font color="#ff0000">-encoding BIG5</font> Hello.java</td>
        </tr>
    </tbody>
</table>
<p>以上需要注意区分源程序的编码与 I/O 操作的编码，前者是在编译时起作用，后者是在运行时起作用。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h4><a name="misunderstand"></a>3. 几种误解，以及乱码产生的原因和解决办法</h4>
<h5>3.1 容易产生的误解</h5>
<table cellspacing="0" cellpadding="3" border="0">
    <tbody>
        <tr>
            <td class="top_1">　</td>
            <td class="top_2" align="center"><strong>对编码的误解</strong></td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">误解一</td>
            <td class="con_2">在将&#8220;字节串&#8221;转化成&#8220;UNICODE 字符串&#8221;时，比如在读取文本文件时，或者通过网络传输文本时，容易将&#8220;字节串&#8221;简单地作为<strong>单字节字符串</strong>，采用每&#8220;一个字节&#8221;就是&#8220;一个字符&#8221;的方法进行转化。<br />
            <br />
            而实际上，在非英文的环境中，应该将&#8220;字节串&#8221;作为 ANSI 字符串，采用适当的编码来得到 UNICODE 字符串，有可能&#8220;多个字节&#8221;才能得到&#8220;一个字符&#8221;。<br />
            <br />
            通常，一直在英文环境下做开发的程序员们，容易有这种误解。</td>
        </tr>
        <tr>
            <td class="bot_1" noWrap align="center">误解二</td>
            <td class="bot_2">在 DOS，Windows 98 等非 UNICODE 环境下，字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串，必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维：&#8220;字符串的编码&#8221;。<br />
            <br />
            当 UNICODE 被支持后，Java 中的 String 是以字符的&#8220;序号&#8221;来存储的，不是以&#8220;某种编码的字节&#8221;来存储的，因此已经不存在&#8220;字符串的编码&#8221;这个概念了。只有在&#8220;字符串&#8221;与&#8220;字节串&#8221;转化时，或者，将一个&#8220;字节串&#8221;当成一个 ANSI 字符串时，才有编码的概念。<br />
            <br />
            不少的人都有这个误解。</td>
        </tr>
    </tbody>
</table>
<p>第一种误解，往往是导致乱码产生的原因。第二种误解，往往导致本来容易纠正的乱码问题变得更复杂。</p>
<p>在这里，我们可以看到，其中所讲的&#8220;误解一&#8221;，即采用每&#8220;一个字节&#8221;就是&#8220;一个字符&#8221;的转化方法，实际上也就等同于采用 iso-8859-1 进行转化。因此，我们常常使用 bytes = string.getBytes("iso-8859-1") 来进行逆向操作，得到原始的&#8220;字节串&#8221;。然后再使用正确的 ANSI 编码，比如 string = new String(bytes, "GB2312")，来得到正确的&#8220;UNICODE 字符串&#8221;。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h5><a name="instances"></a>3.2 非 UNICODE 程序在不同语言环境间移植时的乱码</h5>
<p>非 UNICODE 程序中的字符串，都是以某种 ANSI 编码形式存在的。如果程序运行时的语言环境与开发时的语言环境不同，将会导致 ANSI 字符串的显示失败。</p>
<p>比如，在日文环境下开发的非 UNICODE 的日文程序界面，拿到中文环境下运行时，界面上将显示乱码。如果这个日文程序界面改为采用 UNICODE 来记录字符串，那么当在中文环境下运行时，界面上将可以显示正常的日文。</p>
<p>由于客观原因，有时候我们必须在中文操作系统下运行非 UNICODE 的日文软件，这时我们可以采用一些工具，比如，南极星，AppLocale 等，暂时的模拟不同的语言环境。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h5>3.3 网页提交字符串</h5>
<p>当页面中的表单提交字符串时，首先把字符串按照当前页面的编码，转化成字节串。然后再将每个字节转化成 "%XX" 的格式提交到 Web 服务器。比如，一个编码为 GB2312 的页面，提交 "中" 这个字符串时，提交给服务器的内容为 "%D6%D0"。</p>
<p>在服务器端，Web 服务器把收到的 "%D6%D0" 转化成 [0xD6, 0xD0] 两个字节，然后再根据 GB2312 编码规则得到 "中" 字。</p>
<p>在 Tomcat 服务器中，request.getParameter() 得到乱码时，常常是因为前面提到的&#8220;误解一&#8221;造成的。默认情况下，当提交 "%D6%D0" 给 Tomcat 服务器时，request.getParameter() 将返回 [0x00D6, 0x00D0] 两个 UNICODE 字符，而不是返回一个 "中" 字符。因此，我们需要使用 bytes = string.getBytes("iso-8859-1") 得到原始的字节串，再用 string = new String(bytes, "GB2312") 重新得到正确的字符串 "中"。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h5>3.4 从数据库读取字符串</h5>
<p>通过数据库客户端（比如 ODBC 或 JDBC）从数据库服务器中读取字符串时，客户端需要从服务器获知所使用的 ANSI 编码。当数据库服务器发送字节流给客户端时，客户端负责将字节流按照正确的编码转化成 UNICODE 字符串。</p>
<p>如果从数据库读取字符串时得到乱码，而数据库中存放的数据又是正确的，那么往往还是因为前面提到的&#8220;误解一&#8221;造成的。解决的办法还是通过 string = new String( string.getBytes("iso-8859-1"), "GB2312") 的方法，重新得到原始的字节串，再重新使用正确的编码转化成字符串。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h5>3.5 电子邮件中的字符串</h5>
<p>当一段 Text 或者 HTML 通过电子邮件传送时，发送的内容首先通过一种指定的<strong>字符编码</strong>转化成&#8220;字节串&#8221;，然后再把&#8220;字节串&#8221;通过一种指定的<strong>传输编码</strong>（Content-Transfer-Encoding）进行转化得到另一串&#8220;字节串&#8221;。比如，打开一封电子邮件源代码，可以看到类似的内容：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code">Content-Type: text/plain;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#ff0000">charset="gb2312"</font><br />
            <font color="#ff0000">Content-Transfer-Encoding: base64</font><br />
            <br />
            sbG+qcrQuqO17cf4yee74bGjz9W7+b3wudzA7dbQ0MQNCg0KvPKzxqO6uqO17cnnsaPW0NDEDQoNCg==</td>
        </tr>
    </tbody>
</table>
<p>最常用的 Content-Transfer-Encoding 有 Base64 和 Quoted-Printable 两种。在对二进制文件或者中文文本进行转化时，Base64 得到的&#8220;字节串&#8221;比 Quoted-Printable 更短。在对英文文本进行转化时，Quoted-Printable 得到的&#8220;字节串&#8221;比 Base64 更短。</p>
<p>邮件的标题，用了一种更简短的格式来标注&#8220;字符编码&#8221;和&#8220;传输编码&#8221;。比如，标题内容为 "中"，则在邮件源代码中表示为：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 正确的标题格式</span><br />
            Subject: <span style="background-color: #ffff00">=?</span>GB2312<span style="background-color: #ffff00">?B?</span>1tA=<span style="background-color: #ffff00">?=</span></td>
        </tr>
    </tbody>
</table>
<p>其中，</p>
<ul>
    <li>第一个&#8220;=?&#8221;与&#8220;?&#8221;中间的部分指定了字符编码，在这个例子中指定的是 GB2312。
    <li>&#8220;?&#8221;与&#8220;?&#8221;中间的&#8220;B&#8221;代表 Base64。如果是&#8220;Q&#8221;则代表 Quoted-Printable。
    <li>最后&#8220;?&#8221;与&#8220;?=&#8221;之间的部分，就是经过 GB2312 转化成字节串，再经过 Base64 转化后的标题内容。 </li>
</ul>
<p>如果&#8220;传输编码&#8221;改为 Quoted-Printable，同样，如果标题内容为 "中"：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 正确的标题格式</span><br />
            Subject: <span style="background-color: #ffff00">=?</span>GB2312<span style="background-color: #ffff00">?Q?</span>=D6=D0<span style="background-color: #ffff00">?=</span></td>
        </tr>
    </tbody>
</table>
<p>如果阅读邮件时出现乱码，一般是因为&#8220;字符编码&#8221;或&#8220;传输编码&#8221;指定有误，或者是没有指定。比如，有的发邮件组件在发送邮件时，标题 "中"：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 错误的标题格式</span><br />
            Subject: <span style="background-color: #ffff00">=?</span><font color="#ff0000">ISO-8859-1</font><span style="background-color: #ffff00">?Q?</span>=D6=D0<span style="background-color: #ffff00">?=</span></td>
        </tr>
    </tbody>
</table>
<p>这样的表示，实际上是明确指明了标题为 [0x00D6, 0x00D0]，即 "&#214;&#208;"，而不是 "中"。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="http://www.regexlab.com/images/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.regexlab.com/images/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a></p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<h4><a name="correct"></a>4. 几种错误理解的纠正</h4>
<h5>误解：&#8220;ISO-8859-1 是国际编码？&#8221;</h5>
<p>非也。iso-8859-1 只是单字节字符集中最简单的一种，也就是&#8220;字节编号&#8221;与&#8220;UNICODE 字符编号&#8221;一致的那种编码规则。当我们要把一个&#8220;字节串&#8221;转化成&#8220;字符串&#8221;，而又不知道它是哪一种 ANSI 编码时，先暂时地把&#8220;每一个字节&#8221;作为&#8220;一个字符&#8221;进行转化，不会造成信息丢失。然后再使用 bytes = string.getBytes("iso-8859-1") 的方法可恢复到原始的字节串。</p>
<h5>误解：&#8220;Java 中，怎样知道某个字符串的内码？&#8221;</h5>
<p>Java 中，字符串类 java.lang.String 处理的是 UNICODE 字符串，不是 ANSI 字符串。我们只需要把字符串作为&#8220;抽象的符号的串&#8221;来看待。因此不存在字符串的内码的问题。</p>
 <img src ="http://www.blogjava.net/keweibo/aggbug/144978.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/keweibo/" target="_blank">KE</a> 2007-09-13 22:25 <a href="http://www.blogjava.net/keweibo/articles/144978.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Tomcat中文乱码问题原因和解决方法 </title><link>http://www.blogjava.net/keweibo/articles/144872.html</link><dc:creator>KE</dc:creator><author>KE</author><pubDate>Thu, 13 Sep 2007 07:26:00 GMT</pubDate><guid>http://www.blogjava.net/keweibo/articles/144872.html</guid><wfw:comment>http://www.blogjava.net/keweibo/comments/144872.html</wfw:comment><comments>http://www.blogjava.net/keweibo/articles/144872.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/keweibo/comments/commentRss/144872.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/keweibo/services/trackbacks/144872.html</trackback:ping><description><![CDATA[<div class="postTitle"><a class="postTitle2" id="viewpost1_TitleUrl" href="http://www.blogjava.net/kemi/archive/2007/07/10/129220.html">Tomcat中文乱码问题原因和解决方法</a> </div>
自从接触Java和JSP以来，就不断与Java的中文乱码问题打交道，现在终于得到了彻底的解决，现将我们的解决心得与大家共享。
<p>　　一、Java中文问题的由来</p>
<p>　　Java的内核和class文件是基于unicode的，这使Java程序具有良好的跨平台性，但也带来了一些中文乱码问题的麻烦。原因主要有两方面，Java和JSP文件本身编译时产生的乱码问题和Java程序于其他媒介交互产生的乱码问题。</p>
<p>　　首先Java（包括JSP）源文件中很可能包含有中文，而Java和JSP源文件的保存方式是基于字节流的，如果Java和JSP编译成class文件过程中，使用的编码方式与源文件的编码不一致，就会出现乱码。基于这种乱码，建议在Java文件中尽量不要写中文（注释部分不参与编译，写中文没关系），如果必须写的话，尽量手动带参数－ecoding GBK或－ecoding gb2312编译；对于JSP，在文件头加上&lt;%@ page contentType="text/html;charset=GBK"%&gt;或&lt;%@ page contentType="text/html;charset=gb2312"%&gt;基本上就能解决这类乱码问题。</p>
<p>　　本文要重点讨论的是第二类乱码，即Java程序与其他存储媒介交互时产生的乱码。很多存储媒介，如数据库，文件，流等的存储方式都是基于字节流的，Java程序与这些媒介交互时就会发生字符(char)与字节(byte)之间的转换，例如从页面提交表单中提交的数据在Java程序里显示乱码等情况。</p>
<p>　　如果在以上转换过程中使用的编码方式与字节原有的编码不一致，很可能就会出现乱码。</p>
<p>　　二、解决方法</p>
<p>　　对于流行的Tomcat来说，有以下两种解决方法：</p>
<p>　　1) 更改 D:\Tomcat\conf\server.xml，指定浏览器的编码格式为&#8220;简体中文&#8221;：</p>
<p>　　方法是找到 server.xml 中的</p>
<p>&nbsp;&nbsp;&nbsp; &lt;Connector port="8080" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"<br />
&nbsp;&nbsp;&nbsp; enableLookups="false" redirectPort="8443" acceptCount="100"<br />
&nbsp;&nbsp;&nbsp; connectionTimeout="20000" disableUploadTimeout="true" <strong>URIEncoding='GBK'</strong> /&gt;<br />
</p>
<p>　　标记，粗体字是我添加的。</p>
<p>　　可以这样验证你的更改是否成功：在更改前，在你出现乱码的页面的IE浏览器，点击菜单&#8220;查看｜编码&#8221;，会发现&#8220;西欧(ISO)&#8221;处于选中状态。而更改后，点击菜单&#8220;查看｜编码&#8221;，会发现&#8220;简体中文(GB2312)&#8221;处于选中状态。</p>
<p>　　b)更该 Java 程序，我的程序是这样的：</p>
<p>public class ThreeParams extends HttpServlet {<br />
&nbsp; public void doGet(HttpServletRequest request, HttpServletResponse response)<br />
&nbsp;&nbsp; throws ServletException, IOException {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>response.setContentType("text/html; charset=GBK");<br />
</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br />
&nbsp; }&nbsp; <br />
}<br />
</p>
<p>　　粗体字是必需要有的，它的作用是让浏览器把Unicode字符转换为GBK字符。这样页面的内容和浏览器的显示模式都设成了GBK，就不会乱码了。<br />
<br />
本文来自http://www.blogjava.net/kemi/archive/2007/07/10/129220.html</p>
 <img src ="http://www.blogjava.net/keweibo/aggbug/144872.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/keweibo/" target="_blank">KE</a> 2007-09-13 15:26 <a href="http://www.blogjava.net/keweibo/articles/144872.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编码基本知识</title><link>http://www.blogjava.net/keweibo/articles/144868.html</link><dc:creator>KE</dc:creator><author>KE</author><pubDate>Thu, 13 Sep 2007 07:12:00 GMT</pubDate><guid>http://www.blogjava.net/keweibo/articles/144868.html</guid><wfw:comment>http://www.blogjava.net/keweibo/comments/144868.html</wfw:comment><comments>http://www.blogjava.net/keweibo/articles/144868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/keweibo/comments/commentRss/144868.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/keweibo/services/trackbacks/144868.html</trackback:ping><description><![CDATA[编码基本知识：
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">最早的编码是iso8859-1，和ascii编码相似。但为了方便表示各种各样的语言，逐渐出现了很多<nobr oncontextmenu="return false;" onmousemove="kwM(0);" id="key0" onmouseover="kwE(event,0, this);" style="color: #6600ff; border-bottom: #6600ff 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">标准</nobr>编码，重要的有如下几个:</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">1. iso8859-1</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">属于单字节编码，最多能表示的字符范围是0-255，应用于英文系列。比如，字母a的编码为0x61=97。</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">很明显，iso8859-1<nobr oncontextmenu="return false;" onmousemove="kwM(7);" id="key6" onmouseover="kwE(event,7, this);" style="color: #6600ff; border-bottom: #6600ff 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">编码</nobr>表示的字符范围很窄，无法表示中文字符。但是，由于是单字节编码，和计算机最基础的表示单位一致，所以很多时候，仍旧使用iso8859-1编码来表示。而且在很多协议上，默认使用该编码。比如，虽然"中文"两个字不存在iso8859-1编码，以gb2312编码为例，应该是"d6d0 cec4"两个字符，使用iso8859-1编码的时候则将它拆开为4个字节来表示："d6 d0 ce c4"（事实上，在进行存储的时候，也是以字节为单位处理的）。而如果是UTF编码，则是6个字节"e4 b8 ad e6 96 87"。很明显，这种表示<nobr oncontextmenu="return false;" onmousemove="kwM(3);" id="key3" onmouseover="kwE(event,3, this);" style="color: #6600ff; border-bottom: #6600ff 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">方法</nobr>还需要以另一种编码为基础。</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">2. GB2312/GBK</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">这就是汉子的国标码，专门用来表示汉字，是双字节编码，而英文字母和iso8859-1一致（兼容iso8859-1编码）。其中gbk编码能够用来同时表示繁体字和简体字，而gb2312只能表示简体字，gbk是兼容gb2312编码的。</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">3. unicode</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">这是最统一的编码，可以用来表示所有语言的字符，而且是定长双字节（也有四字节的）编码，包括英文字母在内。所以可以说它是不兼容iso8859-1编码的，也不兼容任何编码。不过，相对于iso8859-1编码来说，uniocode编码只是在前面增加了一个0字节，比如字母a为"00 61"。</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">需要说明的是，定长编码便于计算机处理（注意GB2312/GBK不是定长编码），而unicode又可以用来表示所有字符，所以在很多软件内部是使用unicode编码来处理的，比如<nobr oncontextmenu="return false;" onmousemove="kwM(4);" id="key4" onmouseover="kwE(event,4, this);" style="color: #6600ff; border-bottom: #6600ff 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">java</nobr>。</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">4. UTF</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">考虑到unicode编码不兼容iso8859-1编码，而且容易占用更多的空间：因为对于英文字母，unicode也需要两个字节来表示。所以unicode不便于传输和存储。因此而产生了utf编码，utf编码兼容iso8859-1编码，同时也可以用来表示所有语言的字符，不过，utf编码是不定长编码，每一个字符的长度从1-6个字节不等。另外，utf编码自带<nobr oncontextmenu="return false;" onmousemove="kwM(1);" id="key1" onmouseover="kwE(event,1, this);" style="color: #6600ff; border-bottom: #6600ff 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">简单</nobr>的校验功能。一般来讲，英文字母都是用一个字节表示，而汉字使用三个字节。</p>
<p style="text-indent: 2em"><o:p></o:p></p>
<p style="text-indent: 2em">注意，虽然说utf是为了使用更少的空间而使用的，但那只是相对于unicode编码来说，如果已经知道是<nobr oncontextmenu="return false;" onmousemove="kwM(10);" id="key7" onmouseover="kwE(event,10, this);" style="color: #6600ff; border-bottom: #6600ff 1px dotted; background-color: transparent; text-decoration: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">汉字</nobr>，则使用GB2312/GBK无疑是最节省的。不过另一方面，值得说明的是，虽然utf编码对汉字使用3个字节，但即使对于汉字网页，utf编码也会比unicode编码节省，因为网页中包含了很多的英文字符。<br />
<br />
本文来自<a href="http://digest.softhouse.com.cn/digest/show/28639.html">http://digest.softhouse.com.cn/digest/show/28639.html</a></p>
 <img src ="http://www.blogjava.net/keweibo/aggbug/144868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/keweibo/" target="_blank">KE</a> 2007-09-13 15:12 <a href="http://www.blogjava.net/keweibo/articles/144868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>常用字符集编码的概要特性（二）</title><link>http://www.blogjava.net/keweibo/articles/144859.html</link><dc:creator>KE</dc:creator><author>KE</author><pubDate>Thu, 13 Sep 2007 07:01:00 GMT</pubDate><guid>http://www.blogjava.net/keweibo/articles/144859.html</guid><wfw:comment>http://www.blogjava.net/keweibo/comments/144859.html</wfw:comment><comments>http://www.blogjava.net/keweibo/articles/144859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/keweibo/comments/commentRss/144859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/keweibo/services/trackbacks/144859.html</trackback:ping><description><![CDATA[<p><strong>BIG5</strong></p>
<p>Big5是双字节编码，高字节编码范围是0x81-0xFE，低字节编码范围是0x40-0x7E和0xA1-0xFE。和GBK相比，少了低字节是0x80-0xA0的组合。0x8140-0xA0FE是保留区域，用于用户造字区。</p>
<p>Big5收录的汉字只包括繁体汉字，不包括简体汉字，一些生僻的汉字也没有收录。GBK收录的日文假名字符、俄文字符Big5也没有收录。因为Big5当中收录的字符有限，因此有很多在Big5基础上扩展的编码，如倚天中文系统。Windows系统上使用的代码页CP950也可以理解为是对Big5的扩展，在Big5的基础上增加了7个汉字和一些符号。Big5编码对应的字符集是GBK字符集的子集，也就是说Big5收录的字符是GBK收录字符的一部分，但相同字符的编码不同。</p>
<p>因为Big5也占用了ASCII的编码空间（低字节所使用的0x40-0x7E），所以Big5编码在一些环境下存在和GBK编码相同的问题，即低字节范围为0x40-0x7E的字符有可能会被误处理，尤其是低字节是0x5C（"/"）和0x7C（"|"）的字符。可以参考<strong>GBK</strong>一节相应说明。</p>
<p>尽管有些区别，大多数情况下可以把CP950当作Big5的别名。</p>
<p><strong>ISO-8859-1</strong></p>
<p>ISO-8859-1编码是单字节编码，向下兼容ASCII，其编码范围是0x00-0xFF，0x00-0x7F之间完全和ASCII一致，0x80-0x9F之间是控制字符，0xA0-0xFF之间是文字符号。</p>
<p>ISO-8859-1收录的字符除ASCII收录的字符外，还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。欧元符号出现的比较晚，没有被收录在ISO-8859-1当中。</p>
<p>因为ISO-8859-1编码范围使用了单字节内的所有空间，在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。换言之，把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。这是个很重要的特性，<a href="http://www.mysql.com/">MySQL</a>数据库默认编码是Latin1就是利用了这个特性。ASCII编码是一个7位的容器，ISO-8859-1编码是一个8位的容器。</p>
<p>Latin1是ISO-8859-1的别名，有些环境下写作Latin-1。</p>
<p><strong>UCS-2和UTF-16</strong></p>
<p><a href="http://www.unicode.org/">Unicode</a>组织和<a href="http://www.iso.org/">ISO</a>组织都试图定义一个超大字符集，目的是要涵盖所有语言使用的字符以及其他学科使用的一些特殊符号，这个字符集就是通用字符集（UCS，Universal Character Set）。这两个组织经过协调，虽然在各自发展，但定义的字符位置是完全一致的。ISO相应的标准是ISO 10646。Unicode和ISO 10646都在不断的发展过程中，所以会有不同的版本号来标明不同的发展阶段，每个Unicode版本号都能找到相对应的ISO 10646版本号。</p>
<p>ISO 10646标准定义了一个31位的字符集。前两个字节的位置（0x0000-0xFFFD）被称为基本多语言面（Basic Multilingual Plane, BMP） ，超出两个字节的范围称作辅助语言面。BMP基本包括了所有语言中绝大多数字符，所以只要支持BMP就可以支持绝大多数场合下的应用。Unicode 3.0对应的字符集在BMP范围内。</p>
<p>UCS字符集为每个字符分配了一个位置，通常用&#8220;U&#8221;再加上某个字符在UCS中位置的16进制数作为这个字符的UCS表示，例如&#8220;U+0041&#8221;表示字符&#8220;A&#8221;。UCS字符U+0000到U+00FF与ISO-8859-1完全一致。</p>
<p>UCS-2、UTF-16是UCS字符集（或者说是Unicode字符集）实际应用中的具体编码方式。UCS-2是两个字节的等宽编码，因为只是使用了两个字节的编码空间，所以只能对BMP中的字符做编码。UTF-16是变长编码，用两个字节对BMP内的字符编码，用4个字节对超出BMP范围的辅助平面内的字符作编码。</p>
<p><span>UCS-2不同于GBK和Big5，它是真正的等宽编码，每个字符都使用两个字节，这个特性在字符串截断和字符数计算时非常方便。</span></p>
<p>UTF-16是UCS-2的超集，UTF-16编码的两字节编码方式完全和UCS-2相同，也就是说在BMP的框架内UCS-2完全等同与UTF-16。实际情况当中常常把UCS-16当作UCS-2的别名。</p>
<p>UCS-2和UTF-16在存储和传输时会使用两种不同的字节序，分别是<span id="Contentfont">big endian和little endian（大尾和小尾）。例如&#8220;啊&#8221;（U+554A）用big endian表示就是0x554A，用little endian表示就是0x4A55。UCS-2和UTF-16默认的字节序是big endian方式。在传输过程中为了说明字节序需要在字节流前加上BOM（Byte order Mark），0xFEFF表示是big endian，0xFFFE表示是little endian。UCS-2BE、UCS-2LE是实际应用中使用的编码名称，对应着big endian和little endian，UTF-16BE、UTF-16LE也是如此。因为默认是BE字节序，所以可以把UCS-2当做是UCS-2BE的别名。</span></p>
<blockquote>
<p><span>在UCS编码中有一个叫做&#8220;ZERO WIDTH NO-BREAK SPACE&#8221;的字符，它的编码是U+FEFF，是个没有实际意义的字符。UCS规范建议我们在传输字节流前，先传输字符&#8220;ZERO WIDTH NO-BREAK SPACE&#8221;，如果传输的ZERO WIDTH NO-BREAK SPACE是0xFEFF就说明是big endian，反之就是little endian。</span></p>
</blockquote>
<p><span>UCS-2和UTF-16也可以理解为和ASCII以及ISO-8859-1兼容，在ASCII编码或者ISO-8859-1编码的每个字节前加上0x00，就得到相应字符的UCS-2编码。</span></p>
<p><span>UCS-2和UTF-16中会使用0x00作为某个字符编码的一部分，某些系统会把0x00当作字符串结束的标志，在处理UCS-2或UTF-16编码时会出现问题。</span></p>
<p><strong>UTF-8</strong></p>
<p>UTF-8是UCS字符集的另一种编码方式，UTF-16的每个单元是两个字节（16位），而UTF-8的每个单元是一个字节（8位）。UTF-16中用一个或两个双字节表示一个字符，UTF-8中用一个或几个单字节表示一个字符。</p>
<p>可以认为UTF-8编码是根据一定规律从UCS-2转换得到的，从UCS-2到UTF-8之间有以下转换关系：</p>
<table border="1">
    <tbody>
        <tr>
            <th>UCS-2</th>
            <th>UTF-8</th>
        </tr>
        <tr>
            <td>U+0000 - U+007F</td>
            <td>0<em>xxxxxxx</em></td>
        </tr>
        <tr>
            <td>U+0080 - U+07FF</td>
            <td>110<em>xxxxx</em> 10<em>xxxxxx</em></td>
        </tr>
        <tr>
            <td>U+0800 - U+FFFF</td>
            <td>1110<em>xxxx</em> 10<em>xxxxxx</em> 10<em>xxxxxx</em></td>
        </tr>
    </tbody>
</table>
<p>例如&#8220;啊&#8221;字的UCS-2编码是0x554A，对应的二进制是<strong>0101 0101 0100 1010</strong>，转成UTF-8编码之后的二进制是1110 <strong>0101 </strong>10 <strong>010101 </strong>10 <strong>001010</strong>，对应的十六进制是0xE5958A。</p>
<blockquote>
<p>UCS-4也是一种UCS字符集的编码方式，是使用4个字节的等宽编码，可以用UCS-4来表示BMP之外的辅助面字符。UCS-2中每两个字节前再加上0x0000就得到了BMP字符的UCS-4编码。从UCS-4到UTF-8也存在转换关系，根据这种转换关系，UTF-8最多可以使用六个字节来编码UCS-4。</p>
</blockquote>
<p>根据UTF-8的生成规律和UCS字符集的特性，可以看到UTF-8具有的特性：</p>
<ol>
    <li>UTF-8完全和ASCII兼容，也就是说ASCII对应的字符在UTF-8中和ASCII编码完全一致。范围在0x00-0x7F之内的字符一定是ASCII字符，不可能是其他字符的一部分。GBK和Big5都存在的缺陷在UTF-8中是不存在的。
    <li>大于U+007F的UCS字符，在UTF-8编码中至少是两个字节。
    <li>UTF-8中的每个字符编码的首字节总在0x00-0xFD之间（不考虑UCS-4支持的情况，首字节在0x00-0xEF之间）。根据首字节就可以判断之后连续几个字节。
    <li>非首字节的其他字节都在0x80-0xBF之间；0xFE和0xFF在UTF-8中没有被用到。
    <li>GBK编码中的汉字字符都在UCS-2中的范围都在U+0800 - U+FFFF之间，所以每个GBK编码中的汉字字符的UTF-8编码都是3个字节。但GBK中包含的其他字符的UTF-8编码就不一定是3个字节了，如GBK中的俄文字符。 </li>
</ol>
<p>在UTF-8的编码的传输过程中即使丢掉一个字节，根据编码规律也很容易定位丢掉的位置，不会影响到其他字符。在其他双字节编码中，一旦损失一个字节，就会影响到此字节之后的所有字符。从这点可以看出UTF-8编码非常适合作为传输编码。 </p>
 <img src ="http://www.blogjava.net/keweibo/aggbug/144859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/keweibo/" target="_blank">KE</a> 2007-09-13 15:01 <a href="http://www.blogjava.net/keweibo/articles/144859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>常用字符集编码的概要特性（一）</title><link>http://www.blogjava.net/keweibo/articles/144857.html</link><dc:creator>KE</dc:creator><author>KE</author><pubDate>Thu, 13 Sep 2007 07:00:00 GMT</pubDate><guid>http://www.blogjava.net/keweibo/articles/144857.html</guid><wfw:comment>http://www.blogjava.net/keweibo/comments/144857.html</wfw:comment><comments>http://www.blogjava.net/keweibo/articles/144857.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/keweibo/comments/commentRss/144857.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/keweibo/services/trackbacks/144857.html</trackback:ping><description><![CDATA[<p>搞清常用编码特性是解决字符集编码问题的基础。字符集编码的识别与转换、分析各种乱码产生的原因、编程操作各种编码字符串（例如字符数计算、截断处理）等都需要弄清楚编码的特性。</p>
<p>了解一种字符集编码主要是要了解该编码的编码范围，编码对应的字符集（都包含哪些字符），和其他字符集编码之间的关系等。</p>
<p><strong>ASCII</strong></p>
<p>ASCII码是7位编码，编码范围是0x00-0x7F。ASCII字符集包括英文字母、阿拉伯数字和标点符号等字符。其中0x00-0x20和0x7F共33个控制字符。</p>
<p>只支持ASCII码的系统会忽略每个字节的最高位，只认为低7位是有效位。HZ字符编码就是早期为了在只支持7位ASCII系统中传输中文而设计的编码。早期很多邮件系统也只支持ASCII编码，为了传输中文邮件必须使用BASE64或者其他编码方式。</p>
<p><strong>GB2312</strong></p>
<p>GB2312是基于区位码设计的，区位码把编码表分为94个区，每个区对应94个位，每个字符的区号和位号组合起来就是该汉字的区位码。区位码一般 用10进制数来表示，如1601就表示16区1位，对应的字符是&#8220;啊&#8221;。在区位码的区号和位号上分别加上0xA0就得到了GB2312编码。</p>
<p>区位码中01-09区是符号、数字区，16-87区是汉字区，10-15和88-94是未定义的空白区。它将收录的汉字分成两级：第一级是常用汉字 计3755个，置于16-55区，按汉语拼音字母/笔形顺序排列；第二级汉字是次常用汉字计3008个，置于56-87区，按部首/笔画顺序排列。一级汉 字是按照拼音排序的，这个就可以得到某个拼音在一级汉字区位中的范围，很多根据汉字可以得到拼音的程序就是根据这个原理编写的。</p>
<p>GB2312字符集中除常用简体汉字字符外还包括希腊字母、日文平假名及片假名字母、俄语西里尔字母等字符，未收录繁体中文汉字和一些生僻字。可以用繁体汉字测试某些系统是不是只支持GB2312编码。</p>
<p>GB2312的编码范围是0xA1A1-0x7E7E，去掉未定义的区域之后可以理解为实际编码范围是0xA1A1-0xF7FE。</p>
<p>EUC-CN可以理解为GB2312的别名，和GB2312完全相同。</p>
<blockquote>
<p>区位码更应该认为是字符集的定义，定义了所收录的字符和字符位置，而GB2312及EUC-CN是实际计算机环境中支持这 种字符集的编码。HZ和ISO-2022-CN是对应区位码字符集的另外两种编码，都是用7位编码空间来支持汉字。区位码和GB2312编码的关系有点像 Unicode和UTF-8。</p>
</blockquote>
<p><strong>GBK</strong></p>
<p>GBK编码是GB2312编码的超集，向下完全兼容GB2312，同时GBK收录了Unicode基本多文种平面中的所有CJK汉字。同 GB2312一样，GBK也支持希腊字母、日文假名字母、俄语字母等字符，但不支持韩语中的表音字符（非汉字字符）。GBK还收录了GB2312不包含的 汉字部首符号、竖排标点符号等字符。</p>
<p>GBK的整体编码范围是为0x8140-0xFEFE，不包括低字节是0&#215;7F的组合。高字节范围是0&#215;81-0xFE，低字节范围是0x40-7E和0x80-0xFE。</p>
<p>低字节是0x40-0x7E的GBK字符有一定特殊性，因为这些字符占用了ASCII码的位置，这样会给一些系统带来麻烦。</p>
<blockquote>
<p>有些系统中用0x40-0x7E中的字符（如&#8220;|&#8221;）做特殊符号，在定位这些符号时又没有判断这些符号是不是属于某个 GBK字符的低字节，这样就会造成错误判断。在支持GB2312的环境下就不存在这个问题。需要注意的是支持GBK的环境中小于0x80的某个字节未必就 是ASCII符号；另外就是最好选用小于0&#215;40的ASCII符号做一些特殊符号，这样就可以快速定位，且不用担心是某个汉字的另一半。Big5编码中也存在相应问题。</p>
</blockquote>
<p>CP936和GBK的有些许差别，绝大多数情况下可以把CP936当作GBK的别名。</p>
<p><strong>GB18030</strong></p>
<p>GB18030编码向下兼容GBK和GB2312，兼容的含义是不仅字符兼容，而且相同字符的编码也相同。GB18030收录了所有Unicode3.1中的字符，包括中国少数民族字符，GBK不支持的韩文字符等等，也可以说是世界大多民族的文字符号都被收录在内。</p>
<p>GBK和GB2312都是双字节等宽编码，如果算上和ASCII兼容所支持的单字节，也可以理解为是单字节和双字节混合的变长编码。GB18030编码是变长编码，有单字节、双字节和四字节三种方式。</p>
<p>GB18030的单字节编码范围是0x00-0x7F，完全等同与ASCII；双字节编码的范围和GBK相同，高字节是0x81-0xFE，低字节 的编码范围是0x40-0x7E和0x80-FE；四字节编码中第一、三字节的编码范围是0x81-0xFE，二、四字节是0x30-0x39。</p>
<blockquote>
<p>Windows中CP936代码页使用0x80来表示欧元符号，而在GB18030编码中没有使用0x80编码位，用其他位置来表示欧元符号。这可以理解为是GB18030向下兼容性上的一点小问题；也可以理解为0x80是CP936对GBK的扩展，而GB18030只是和GBK兼容良好。</p>
</blockquote>
 <img src ="http://www.blogjava.net/keweibo/aggbug/144857.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/keweibo/" target="_blank">KE</a> 2007-09-13 15:00 <a href="http://www.blogjava.net/keweibo/articles/144857.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>