﻿<?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-Good good study,day day play!</title><link>http://www.blogjava.net/happytian/</link><description>My really life......</description><language>zh-cn</language><lastBuildDate>Tue, 12 May 2026 22:58:39 GMT</lastBuildDate><pubDate>Tue, 12 May 2026 22:58:39 GMT</pubDate><ttl>60</ttl><item><title>java 关键字</title><link>http://www.blogjava.net/happytian/archive/2008/05/14/200476.html</link><dc:creator>happytian</dc:creator><author>happytian</author><pubDate>Wed, 14 May 2008 12:50:00 GMT</pubDate><guid>http://www.blogjava.net/happytian/archive/2008/05/14/200476.html</guid><wfw:comment>http://www.blogjava.net/happytian/comments/200476.html</wfw:comment><comments>http://www.blogjava.net/happytian/archive/2008/05/14/200476.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/happytian/comments/commentRss/200476.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/happytian/services/trackbacks/200476.html</trackback:ping><description><![CDATA[关键字列表 <br />
<br />
abstract&nbsp;boolean&nbsp;break&nbsp;byte&nbsp;case <br />
catch&nbsp;char&nbsp;class&nbsp;continue&nbsp;default <br />
do&nbsp;double&nbsp;else&nbsp;extends&nbsp;false <br />
final&nbsp;finally&nbsp;float&nbsp;for&nbsp;if <br />
implements&nbsp;import&nbsp;instanceof&nbsp;int&nbsp;interface <br />
long&nbsp;native&nbsp;new&nbsp;null&nbsp;package <br />
private&nbsp;protected&nbsp;public&nbsp;return&nbsp;short <br />
static&nbsp;super&nbsp;switch&nbsp;synchronized&nbsp;this <br />
throw&nbsp;throws&nbsp;transient&nbsp;true&nbsp;try <br />
void&nbsp;volatile&nbsp;while&nbsp; <br />
<br />
保留字 <br />
&amp;n&nbsp;bsp;const，goto <br />
<br />
注意点 <br />
<br />
识别java语言的关键字，不要和其他语言如c/c++的关键字混淆。 <br />
&nbsp; <br />
const和goto是java的保留字。 <br />
所有的关键字都是小写 <br />
friendly，sizeof不是java的关键字</font> 　 <br />
<br />
<br />
1. 标识符变量,类与方法都需要一定的名称,我们将这种名称叫做标识符.java中对标识符有一定的限制.首先:所有的标识符的首字符必须是字母（大小写）.下划线＿或美元符￥;其次标示符是由数字（0--9）,所有从a--z的大写字母.a--z的小写字母与下划线＿.美元符￥与所有在十六进制0xc0前的 ascii码等构成;第三注意表示符不能使用系统保留的关键字做标示符. 
<img src ="http://www.blogjava.net/happytian/aggbug/200476.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/happytian/" target="_blank">happytian</a> 2008-05-14 20:50 <a href="http://www.blogjava.net/happytian/archive/2008/05/14/200476.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java 调用存储过程</title><link>http://www.blogjava.net/happytian/archive/2008/05/14/200472.html</link><dc:creator>happytian</dc:creator><author>happytian</author><pubDate>Wed, 14 May 2008 12:19:00 GMT</pubDate><guid>http://www.blogjava.net/happytian/archive/2008/05/14/200472.html</guid><wfw:comment>http://www.blogjava.net/happytian/comments/200472.html</wfw:comment><comments>http://www.blogjava.net/happytian/archive/2008/05/14/200472.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/happytian/comments/commentRss/200472.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/happytian/services/trackbacks/200472.html</trackback:ping><description><![CDATA[本文阐述了怎么使用DBMS存储过程。我阐述了使用存储过程的基本的和高级特性，比如返回ResultSet。本文假设你对DBMS和JDBC已经非常熟悉，也假设你能够毫无障碍地阅读其它语言写成的代码（即不是Java的语言），但是，并不要求你有任何存储过程的编程经历。 <br />
存储过程是指保存在数据库并在数据库端执行的程序。你可以使用特殊的语法在Java类中调用存储过程。在调用时，存储过程的名称及指定的参数通过JDBC连接发送给DBMS，执行存储过程并通过连接（如果有）返回结果。 <br />
使用存储过程拥有和使用基于EJB或CORBA这样的应用服务器一样的好处。区别是存储过程可以从很多流行的DBMS中免费使用，而应用服务器大都非常昂贵。这并不只是许可证费用的问题。使用应用服务器所需要花费的管理、编写代码的费用，以及客户程序所增加的复杂性，都可以通过DBMS中的存储过程所整个地替代。 <br />
你可以使用Java，Python，Perl或C编写存储过程，但是通常使用你的DBMS所指定的特定语言。Oracle使用PL/SQL，PostgreSQL使用pl/pgsql，DB2使用Procedural SQL。这些语言都非常相似。在它们之间移植存储过程并不比在Sun的EJB规范不同实现版本之间移植Session Bean困难。并且，存储过程是为嵌入SQL所设计，这使得它们比Java或C等语言更加友好地方式表达数据库的机制。 <br />
因为存储过程运行在DBMS自身，这可以帮助减少应用程序中的等待时间。不是在Java代码中执行4个或5个SQL语句，而只需要在服务器端执行1个存储过程。网络上的数据往返次数的减少可以戏剧性地优化性能。 <br />
<br />
使用存储过程 <br />
<br />
简单的老的JDBC通过CallableStatement类支持存储过程的调用。该类实际上是PreparedStatement的一个子类。假设我们有一个poets数据库。数据库中有一个设置诗人逝世年龄的存储过程。下面是对老酒鬼Dylan Thomas（old soak Dylan Thomas，不指定是否有关典故、文化，请批评指正。译注）进行调用的详细代码： <br />
<br />
try{<br />
<br />
int age = 39; <br />
<br />
String poetName = "dylan thomas"; <br />
<br />
CallableStatement proc = connection.prepareCall("{ call set_death_age(?, ?) }"); <br />
<br />
proc.setString(1, poetName); <br />
<br />
proc.setInt(2, age); <br />
<br />
cs.execute();<br />
<br />
}catch (SQLException e){ // ....} <br />
<br />
传给prepareCall方法的字串是存储过程调用的书写规范。它指定了存储过程的名称，？代表了你需要指定的参数。 <br />
和JDBC集成是存储过程的一个很大的便利：为了从应用中调用存储过程，不需要存根（stub）类或者配置文件，除了你的DBMS的JDBC驱动程序外什么也不需要。 <br />
当这段代码执行时，数据库的存储过程就被调用。我们没有去获取结果，因为该存储过程并不返回结果。执行成功或失败将通过例外得知。失败可能意味着调用存储过程时的失败（比如提供的一个参数的类型不正确），或者一个应用程序的失败（比如抛出一个例外指示在poets数据库中并不存在&#8220;Dylan Thomas&#8221;） <br />
<br />
结合SQL操作与存储过程 <br />
<br />
映射Java对象到SQL表中的行相当简单，但是通常需要执行几个SQL语句；可能是一个SELECT查找ID，然后一个INSERT插入指定ID的数据。在高度规格化（符合更高的范式，译注）的数据库模式中，可能需要多个表的更新，因此需要更多的语句。Java代码会很快地膨胀，每一个语句的网络开销也迅速增加。 <br />
将这些SQL语句转移到一个存储过程中将大大简化代码，仅涉及一次网络调用。所有关联的SQL操作都可以在数据库内部发生。并且，存储过程语言，例如PL/SQL，允许使用SQL语法，这比Java代码更加自然。下面是我们早期的存储过程，使用Oracle的PL/SQL语言编写： <br />
<br />
create procedure set_death_age(poet VARCHAR2, poet_age NUMBER) <br />
<br />
poet_id NUMBER;<br />
<br />
begin SELECT id INTO poet_id FROM poets WHERE name = poet; <br />
<br />
INSERT INTO deaths (mort_id, age) VALUES (poet_id, poet_age);<br />
<br />
end set_death_age; <br />
<br />
很独特？不。我打赌你一定期待看到一个poets表上的UPDATE。这也暗示了使用存储过程实现是多么容易的一件事情。set_death_age几乎可以肯定是一个很烂的实现。我们应该在poets表中添加一列来存储逝世年龄。Java代码中并不关心数据库模式是怎么实现的，因为它仅调用存储过程。我们以后可以改变数据库模式以提高性能，但是我们不必修改我们代码。 <br />
下面是调用上面存储过程的Java代码： <br />
<br />
public static void setDeathAge(Poet dyingBard, int age) throws SQLException{ <br />
<br />
Connection con = null; <br />
<br />
CallableStatement proc = null; <br />
<br />
try {<br />
<br />
con = connectionPool.getConnection(); <br />
<br />
proc = con.prepareCall("{ call set_death_age(?, ?) }");<br />
<br />
proc.setString(1, dyingBard.getName()); <br />
<br />
proc.setInt(2, age); <br />
<br />
proc.execute(); <br />
<br />
} <br />
<br />
finally { <br />
<br />
try { proc.close(); } <br />
<br />
catch (SQLException e) {} <br />
<br />
con.close(); <br />
<br />
}<br />
<br />
} <br />
<br />
为了确保可维护性，建议使用像这儿这样的static方法。这也使得调用存储过程的代码集中在一个简单的模版代码中。如果你用到许多存储过程，就会发现仅需要拷贝、粘贴就可以创建新的方法。因为代码的模版化，甚至也可以通过脚本自动生产调用存储过程的代码。 <br />
<br />
Functions <br />
<br />
存储过程可以有返回值，所以CallableStatement类有类似getResultSet这样的方法来获取返回值。当存储过程返回一个值时，你必须使用registerOutParameter方法告诉JDBC驱动器该值的SQL类型是什么。你也必须调整存储过程调用来指示该过程返回一个值。 <br />
下面接着上面的例子。这次我们查询Dylan Thomas逝世时的年龄。这次的存储过程使用PostgreSQL的pl/pgsql： <br />
<br />
create function snuffed_it_when (VARCHAR) returns integer &#180;declare <br />
<br />
poet_id NUMBER; <br />
<br />
poet_age NUMBER;<br />
<br />
begin <br />
<br />
--first get the id associated with the poet. <br />
<br />
SELECT id INTO poet_id FROM poets WHERE name = $1;<br />
<br />
--get and return the age. <br />
<br />
SELECT age INTO poet_age FROM deaths WHERE mort_id = poet_id; <br />
<br />
return age;<br />
<br />
end;&#180; language &#180;pl/pgsql&#180;; <br />
<br />
另外，注意pl/pgsql参数名通过Unix和DOS脚本的$n语法引用。同时，也注意嵌入的注释，这是和Java代码相比的另一个优越性。在Java中写这样的注释当然是可以的，但是看起来很凌乱，并且和SQL语句脱节，必须嵌入到Java String中。 <br />
下面是调用这个存储过程的Java代码： <br />
<br />
connection.setAutoCommit(false);<br />
<br />
CallableStatement proc = connection.prepareCall("{ ? = call snuffed_it_when(?) }");<br />
<br />
proc.registerOutParameter(1, Types.INTEGER);<br />
<br />
proc.setString(2, poetName);<br />
<br />
cs.execute();<br />
<br />
int age = proc.getInt(2); <br />
<br />
如果指定了错误的返回值类型会怎样？那么，当调用存储过程时将抛出一个RuntimeException，正如你在ResultSet操作中使用了一个错误的类型所碰到的一样。 <br />
<br />
复杂的返回值 <br />
<br />
关于存储过程的知识，很多人好像就熟悉我们所讨论的这些。如果这是存储过程的全部功能，那么存储过程就不是其它远程执行机制的替换方案了。存储过程的功能比这强大得多。 <br />
当你执行一个SQL查询时，DBMS创建一个叫做cursor（游标）的数据库对象，用于在返回结果中迭代每一行。ResultSet是当前时间点的游标的一个表示。这就是为什么没有缓存或者特定数据库的支持，你只能在ResultSet中向前移动。 <br />
某些DBMS允许从存储过程中返回游标的一个引用。JDBC并不支持这个功能，但是Oracle、PostgreSQL和DB2的JDBC驱动器都支持在ResultSet上打开到游标的指针（pointer）。 <br />
设想列出所有没有活到退休年龄的诗人，下面是完成这个功能的存储过程，返回一个打开的游标，同样也使用PostgreSQL的pl/pgsql语言： <br />
<br />
create procedure list_early_deaths () return refcursor as &#180;declare <br />
<br />
toesup refcursor;<br />
<br />
begin <br />
<br />
open toesup for SELECT poets.name, deaths.age FROM poets, deaths -- all entries in deaths are for poets. -- but the table might become generic. <br />
<br />
WHERE poets.id = deaths.mort_id AND deaths.age &lt; 60; <br />
<br />
return toesup;<br />
<br />
end;&#180; language &#180;plpgsql&#180;; <br />
<br />
下面是调用该存储过程的Java方法，将结果输出到PrintWriter： <br />
PrintWriter: <br />
<br />
static void sendEarlyDeaths(PrintWriter out){ <br />
<br />
Connection con = null;<br />
<br />
CallableStatement toesUp = null;<br />
<br />
try {<br />
<br />
con = ConnectionPool.getConnection(); <br />
<br />
// PostgreSQL needs a transaction to do this... con.<br />
<br />
setAutoCommit(false); // Setup the call. <br />
<br />
CallableStatement toesUp = connection.prepareCall("{ ? = call list_early_deaths () }"); <br />
<br />
toesUp.registerOutParameter(1, Types.OTHER); <br />
<br />
toesUp.execute(); <br />
<br />
ResultSet rs = (ResultSet) toesUp.getObject(1); <br />
<br />
while (rs.next()) {<br />
<br />
String name = rs.getString(1);<br />
<br />
int age = rs.getInt(2); <br />
<br />
out.println(name + " was " + age + " years old."); <br />
<br />
} <br />
<br />
rs.close(); <br />
<br />
} <br />
<br />
catch (SQLException e) { // We should protect these calls. toesUp.close(); con.close();<br />
<br />
}<br />
<br />
} <br />
<br />
因为JDBC并不直接支持从存储过程中返回游标，我们使用Types.OTHER来指示存储过程的返回类型，然后调用getObject()方法并对返回值进行强制类型转换。 <br />
这个调用存储过程的Java方法是mapping的一个好例子。Mapping是对一个集上的操作进行抽象的方法。不是在这个过程上返回一个集，我们可以把操作传送进去执行。本例中，操作就是把ResultSet打印到一个输出流。这是一个值得举例的很常用的例子，下面是调用同一个存储过程的另外一个方法实现： <br />
<br />
public class ProcessPoetDeaths{ <br />
<br />
public abstract void sendDeath(String name, int age);<br />
<br />
}<br />
<br />
static void mapEarlyDeaths(ProcessPoetDeaths mapper){ <br />
<br />
Connection con = null; <br />
<br />
CallableStatement toesUp = null; <br />
<br />
try {<br />
<br />
con = ConnectionPool.getConnection(); <br />
<br />
con.setAutoCommit(false); <br />
<br />
CallableStatement toesUp = connection.prepareCall("{ ? = call list_early_deaths () }");<br />
<br />
toesUp.registerOutParameter(1, Types.OTHER); <br />
<br />
toesUp.execute(); <br />
<br />
ResultSet rs = (ResultSet) toesUp.getObject(1); <br />
<br />
while (rs.next()) { <br />
<br />
String name = rs.getString(1); <br />
<br />
int age = rs.getInt(2); <br />
<br />
mapper.sendDeath(name, age); <br />
<br />
} <br />
<br />
rs.close(); <br />
<br />
} catch (SQLException e) { // We should protect these calls. toesUp.close(); <br />
<br />
con.close(); <br />
<br />
}<br />
<br />
} <br />
<br />
这允许在ResultSet数据上执行任意的处理，而不需要改变或者复制获取ResultSet的方法： <br />
<br />
static void sendEarlyDeaths(final PrintWriter out){ <br />
<br />
ProcessPoetDeaths myMapper = new ProcessPoetDeaths() { <br />
<br />
public void sendDeath(String name, int age) { <br />
<br />
out.println(name + " was " + age + " years old."); <br />
<br />
} <br />
<br />
}; <br />
<br />
mapEarlyDeaths(myMapper);<br />
<br />
} <br />
<br />
这个方法使用ProcessPoetDeaths的一个匿名实例调用mapEarlyDeaths。该实例拥有sendDeath方法的一个实现，和我们上面的例子一样的方式把结果写入到输出流。当然，这个技巧并不是存储过程特有的，但是和存储过程中返回的ResultSet结合使用，是一个非常强大的工具。 <br />
<br />
结论 <br />
<br />
存储过程可以帮助你在代码中分离逻辑，这基本上总是有益的。这个分离的好处有： <br />
&#8226; 快速创建应用，使用和应用一起改变和改善的数据库模式。 <br />
&#8226; 数据库模式可以在以后改变而不影响Java对象，当我们完成应用后，可以重新设计更好的模式。 <br />
&#8226; 存储过程通过更好的SQL嵌入使得复杂的SQL更容易理解。 <br />
&#8226; 编写存储过程比在Java中编写嵌入的SQL拥有更好的工具－－大部分编辑器都提供语法高亮！ <br />
&#8226; 存储过程可以在任何SQL命令行中测试，这使得调试更加容易。 <br />
<br />
并不是所有的数据库都支持存储过程，但是存在许多很棒的实现，包括免费/开源的和非免费的，所以移植并不是一个问题。Oracle、PostgreSQL和DB2都有类似的存储过程语言，并且有在线的社区很好地支持。 <br />
存储过程工具很多，有像TOAD或TORA这样的编辑器、调试器和IDE，提供了编写、维护PL/SQL或pl/pgsql的强大的环境。 <br />
存储过程确实增加了你的代码的开销，但是它们和大多数的应用服务器相比，开销小得多。如果你的代码复杂到需要使用DBMS，我建议整个采用存储过程的方式。 <br />
<br />
资源 <br />
<br />
&#8226; JDBC specification <br />
&#8226; PostgreSQL <br />
&#8226; Oracle Corporation&#180;s Oracle database server <br />
&#8226; IBM&#180;s DB2 database server <br />
<br />
作者简介：Nic Ferrier 是Web应用方面的独立软件顾问。顾问。 <br />
<img src ="http://www.blogjava.net/happytian/aggbug/200472.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/happytian/" target="_blank">happytian</a> 2008-05-14 20:19 <a href="http://www.blogjava.net/happytian/archive/2008/05/14/200472.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符，字节和编码</title><link>http://www.blogjava.net/happytian/archive/2007/10/24/155453.html</link><dc:creator>happytian</dc:creator><author>happytian</author><pubDate>Wed, 24 Oct 2007 01:33:00 GMT</pubDate><guid>http://www.blogjava.net/happytian/archive/2007/10/24/155453.html</guid><wfw:comment>http://www.blogjava.net/happytian/comments/155453.html</wfw:comment><comments>http://www.blogjava.net/happytian/archive/2007/10/24/155453.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/happytian/comments/commentRss/155453.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/happytian/services/trackbacks/155453.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>&nbsp;</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" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></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" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#000080"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></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" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></td>
            <td><img height="1" alt="" src="http://www.regexlab.com/images/spacer.gif" width="1" border="0" /></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>&nbsp;</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"><font color="#339933">（占7字节）</font></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"><font color="#339933">（占10字节）</font></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>&nbsp;</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>&nbsp;</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>&nbsp;</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%">&nbsp;</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"><font color="#339933">// ANSI 字符串，内容长度 7 字节</font></span><span class="key"><br />
            <font color="#0000ff">char</font></span>&nbsp;&nbsp;&nbsp;&nbsp; sz[<span class="number"><font color="#6d66a5">20</font></span>] = <span class="string"><font color="#ff00ff">"中文123"</font></span>;<br />
            <br />
            <font color="#339933"><span class="rem">// UNICODE 字符串，内容长度 5 个 wchar_t（10 字节）</span><br />
            </font>wchar_t wsz[<span class="number"><font color="#6d66a5">20</font></span>] = L<span class="string"><font color="#ff00ff">"\x4E2D\x6587\x0031\x0032\x0033"</font></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"><font color="#339933">// 运行时设定当前 ANSI 编码，VC 格式<br />
            </font></span>setlocale(LC_ALL, <span class="string"><font color="#ff00ff">".936"</font></span>);<br />
            <br />
            <font color="#339933"><span class="rem">// GCC 中格式</span><br />
            </font>setlocale(LC_ALL, <span class="string"><font color="#ff00ff">"zh_CN.GBK"</font></span>);<br />
            <br />
            <font color="#339933"><span class="rem">// Visual C++ 中使用小写 %s，按照 setlocale 指定编码输出到文件<br />
            // GCC 中使用大写 %S</span><br />
            </font>fwprintf(fp, L<span class="string"><font color="#ff00ff">"%s\n"</font></span>, wsz);<br />
            <br />
            <font color="#339933"><span class="rem">// 把 UNICODE 字符串按照 setlocale 指定的编码转换成字节</span><br />
            </font>wcstombs(sz, wsz, <span class="number"><font color="#6d66a5">20</font></span>);<span class="rem"><br />
            <font color="#339933">// 把字节串按照 setlocale 指定的编码转换成 UNICODE 字符串<br />
            </font></span>mbstowcs(wsz, sz, <span class="number"><font color="#6d66a5">20</font></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"><font color="#339933">// 如果源程序的编码与当前默认 ANSI 编码不一致，<br />
            // 则需要此行，编译时用来指明当前源程序使用的编码</font></span><span class="key"><br />
            <font color="#0000ff">#pragma setlocale</font></span>(<span class="string"><font color="#ff00ff">".936"</font></span>)<br />
            <br />
            <font color="#339933"><span class="rem">// UNICODE 字符串常量，内容长度 10 字节</span><br />
            </font>wchar_t wsz[<span class="number"><font color="#6d66a5">20</font></span>] = L<span class="string"><font color="#ff00ff">"中文123"</font></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%">&nbsp;</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"><font color="#339933">// Java 代码，直接写中文</font></span><span class="pw"><br />
            <font color="#ff0000">String</font></span> string = <span class="string"><font color="#ff00ff">"中文123"</font></span>;<br />
            <br />
            <font color="#339933"><span class="rem">// 得到长度为 5，因为是 5 个字符</span><br />
            </font><span class="pw"><font color="#ff0000">System</font></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"><font color="#339933">// 字符串与字节串间相互转化<br />
            <br />
            // 按照 GB2312 得到字节（得到多字节字符串）</font></span><span class="key"><br />
            <font color="#0000ff">byte</font></span> [] bytes = string.getBytes(<span class="string"><font color="#ff00ff">"GB2312"</font></span>);<br />
            <br />
            <font color="#339933"><span class="rem">// 从字节按照 GB2312 得到 UNICODE 字符串</span><br />
            </font>string = <span class="key"><font color="#0000ff">new</font></span> <span class="pw"><font color="#ff0000">String</font></span>(bytes, <span class="string"><font color="#ff00ff">"GB2312"</font></span>);<br />
            <br />
            <font color="#339933"><span class="rem">// 要将 String 按照某种编码写入文本文件，有两种方法：<br />
            <br />
            // 第一种办法：用 Stream 类写入已经按照指定编码转化好的字节串</span><br />
            </font>OutputStream os = <span class="key"><font color="#0000ff">new</font></span> FileOutputStream(<span class="string"><font color="#ff00ff">"1.txt"</font></span>);<br />
            os.write(bytes);<br />
            os.close();<br />
            <br />
            <font color="#339933"><span class="rem">// 第二种办法：构造指定编码的 Writer 来写入字符串</span><br />
            </font>Writer ow = <span class="key"><font color="#0000ff">new</font></span> OutputStreamWriter(<span class="key"><font color="#0000ff">new</font></span> FileOutputStream(<span class="string"><font color="#ff00ff">"2.txt"</font></span>), <span class="string"><font color="#ff00ff">"GB2312"</font></span>);<br />
            ow.write(string);<br />
            ow.close();<br />
            <br />
            <span class="rem"><font color="#339933">/* 最后得到的 1.txt 和 2.txt 都是 7 个字节 */</font></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>&nbsp;</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>&nbsp;</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>&nbsp;</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%">&nbsp;</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>&nbsp;</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"><font color="#339933"><span class="rem">// 正确的标题格式</span><br />
            </font>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"><font color="#339933"><span class="rem">// 正确的标题格式</span><br />
            </font>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"><font color="#339933"><span class="rem">// 错误的标题格式</span><br />
            </font>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>&nbsp;</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>
<p>　</p>
<img src ="http://www.blogjava.net/happytian/aggbug/155453.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/happytian/" target="_blank">happytian</a> 2007-10-24 09:33 <a href="http://www.blogjava.net/happytian/archive/2007/10/24/155453.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>消除jsp中get方法传递中文参数的乱码问题</title><link>http://www.blogjava.net/happytian/archive/2007/08/27/139739.html</link><dc:creator>happytian</dc:creator><author>happytian</author><pubDate>Mon, 27 Aug 2007 01:43:00 GMT</pubDate><guid>http://www.blogjava.net/happytian/archive/2007/08/27/139739.html</guid><wfw:comment>http://www.blogjava.net/happytian/comments/139739.html</wfw:comment><comments>http://www.blogjava.net/happytian/archive/2007/08/27/139739.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/happytian/comments/commentRss/139739.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/happytian/services/trackbacks/139739.html</trackback:ping><description><![CDATA[首先，进入tomcat目录（:/usr/local/jakarta-tomcat-5.0.28/conf)<br>sudo vim server.xml<br>在如下添加红色部分<br>&nbsp;&lt;!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8080 --&gt;<br>&nbsp;&nbsp;&nbsp; &lt;Connector port="8080"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxThreads="150" minSpareThreads="25" maxSpareThreads="75"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enableLookups="false" redirectPort="8443" acceptCount="100"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; debug="0" connectionTimeout="20000"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; disableUploadTimeout="true" <span style="COLOR: rgb(255,1,2)">URIEncoding="UTF-8或GBK"</span>/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;!-- Note : To disable connection timeouts, set connectionTimeout value<br>&nbsp;&nbsp;&nbsp;&nbsp; to 0 --&gt;<br>保存，重启tomcat即可<br><font size=4>注意</font><br>统一资源标识符 (Uniform Resource Identifier, URI) <br>统一资源定位符 (Uniform Resource Locator, URL)
<img src ="http://www.blogjava.net/happytian/aggbug/139739.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/happytian/" target="_blank">happytian</a> 2007-08-27 09:43 <a href="http://www.blogjava.net/happytian/archive/2007/08/27/139739.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>登录LDAP </title><link>http://www.blogjava.net/happytian/archive/2007/08/22/138565.html</link><dc:creator>happytian</dc:creator><author>happytian</author><pubDate>Wed, 22 Aug 2007 03:58:00 GMT</pubDate><guid>http://www.blogjava.net/happytian/archive/2007/08/22/138565.html</guid><wfw:comment>http://www.blogjava.net/happytian/comments/138565.html</wfw:comment><comments>http://www.blogjava.net/happytian/archive/2007/08/22/138565.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/happytian/comments/commentRss/138565.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/happytian/services/trackbacks/138565.html</trackback:ping><description><![CDATA[1）先用hashTable初始化ldap用到的参数。<br>Hashtable&nbsp;env&nbsp;=&nbsp;new&nbsp;Hashtable();<br>env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");<br>env.put(Context.PROVIDER_URL,"ldap://192.***.***.***:portNumber(389&nbsp;is&nbsp;defualt)"）;<br>env.put(Context.SECURITY_PRINCIPAL,userID+"yourDomain(@***.com)");//<br>env.put(Context.SECURITY_CREDENTIALS,&nbsp;pass);<br>userID&nbsp;和&nbsp;pass就是登录域的账号。不要用全称。因为你在上面用到了域名。<br>2）开始验证。通过try..catch来判断用户的合法性。<br>try{<br>LdapContext&nbsp;ctx&nbsp;=&nbsp;new&nbsp;InitialLdapContext(env,&nbsp;null);<br>System.out.println("Succeess");<br>}&nbsp;catch&nbsp;（Exception&nbsp;e)&nbsp;{<br>if&nbsp;(e.getMessage().indexOf("775")&nbsp;&gt;&nbsp;=&nbsp;0)&nbsp;{<br>System.out.println("你的账号被锁了。");<br>}else&nbsp;{<br>System.out.println("Invalid&nbsp;User");<br>}<br>}
<img src ="http://www.blogjava.net/happytian/aggbug/138565.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/happytian/" target="_blank">happytian</a> 2007-08-22 11:58 <a href="http://www.blogjava.net/happytian/archive/2007/08/22/138565.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>