<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>idv2 &#187; unicode</title>
	<atom:link href="http://tech.idv2.com/tag/unicode/feed/" rel="self" type="application/rss+xml" />
	<link>http://tech.idv2.com</link>
	<description>关注Web开发技术，关注Internet。</description>
	<lastBuildDate>Tue, 27 Jul 2010 12:54:54 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Unicode详解</title>
		<link>http://tech.idv2.com/2008/02/21/unicode-intro/</link>
		<comments>http://tech.idv2.com/2008/02/21/unicode-intro/#comments</comments>
		<pubDate>Thu, 21 Feb 2008 13:39:40 +0000</pubDate>
		<dc:creator>charlee</dc:creator>
				<category><![CDATA[web]]></category>
		<category><![CDATA[encode]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[unicode]]></category>
		<category><![CDATA[utf8]]></category>

		<guid isPermaLink="false">http://tech.idv2.com/2008/02/21/unicode-intro/</guid>
		<description><![CDATA[<!-- begin Pukiwiki generated code--><p>最近一直在忙点私活，又好久没写blog了，再不写点的话二月份就又要以单篇文章结束了。
前一阵子一直在研究Unicode，索性把研究结果介绍一下吧。</p>
<p>可能大家都听说过 Unicode、UCS-2、UTF-8 等等词汇，但它们具体是什么意思，
是什么原理，之间有什么关系，恐怕就很少有人明白了。
下面就分别介绍一下它们。</p>
<!-- end Pukiwiki generated code--><span id="more-570"></span><!-- begin Pukiwiki generated code--><hr class="full_hr" />
<div class="contents">
<a id="contents_2"></a>
<ul class="list1" style="padding-left:16px;margin-left:16px"><li><a href="#content_2_0">  基本知识</a>
<ul class="list2" style="padding-left:16px;margin-left:16px"><li><a href="#content_2_1">  字节和字符的区别</a></li>
<li><a href="#content_2_2">  Big Endian和Little Endian</a></li></ul></li>
<li><a href="#content_2_3">  UCS-2和UCS-4</a></li>
<li><a href="#content_2_4">  UTF-16和UTF-32</a>
<ul class="list2" style="padding-left:16px;margin-left:16px"><li><a href="#content_2_5">  UTF-16</a></li>
<li><a href="#content_2_6">  UTF-32</a></li></ul></li>
<li><a href="#content_2_7">  UTF-8</a></li></ul>
</div>

<h2 id="content_2_0">基本知识</h2>
<p>介绍Unicode之前，首先要讲解一些基础知识。虽然跟Unicode没有直接的关系，
但想弄明白Unicode，没这些还真不行。</p>

<h3 id="content_2_1">字节和字符的区别</h3>
<p>咦，字节和字符能有什么区别啊？不都是一样的吗？完全正确，但只是在古老的DOS时代。
当Unicode出现后，字节和字符就不一样了。</p>
<p>字节（octet）是一个八位的存储单元，取值范围一定是0～255。而字符（character，或者word）
为语言意义上的符号，范围就不一定了。例如在UCS-2中定义的字符范围为0～65535，
它的一个字符占用两个字节。</p>

<h3 id="content_2_2">Big Endian和Little Endian</h3>
<p>上面提到了一个字符可能占用多个字节，那么这多个字节在计算机中如何存储呢？
比如字符0xabcd，它的存储格式到底是 AB CD，还是 CD AB 呢？</p>
<p>实际上两者都有可能，并分别有不同的名字。如果存储为 AB CD，则称为<strong>Big Endian</strong>；
如果存储为 CD AB，则称为<strong>Little Endian</strong>。</p>
<p>具体来说，以下这种存储格式为Big Endian，因为值(0xabcd)的高位(0xab)存储在前面：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">地址</td><td class="style_td">值</td></tr><tr><td class="style_td">0x00000000</td><td class="style_td">AB</td></tr><tr><td class="style_td">0x00000001</td><td class="style_td">CD</td></tr></tbody></table></div>
<p>相反，以下这种存储格式为Little Endian：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">地址</td><td class="style_td">值</td></tr><tr><td class="style_td">0x00000000</td><td class="style_td">CD</td></tr><tr><td class="style_td">0x00000001</td><td class="style_td">AB</td></tr></tbody></table></div>

<h2 id="content_2_3">UCS-2和UCS-4</h2>
<p>Unicode是为整合全世界的所有语言文字而诞生的。任何文字在Unicode中都对应一个值，
这个值称为<strong>代码点</strong>（code point）。代码点的值通常写成 U+ABCD 的格式。
而文字和代码点之间的对应关系就是<strong>UCS-2</strong>（Universal Character Set coded in 2 octets）。
顾名思义，UCS-2是用两个字节来表示代码点，其取值范围为 U+0000～U+FFFF。</p>
<p>为了能表示更多的文字，人们又提出了UCS-4，即用四个字节表示代码点。
它的范围为 U+00000000～U+7FFFFFFF，其中 U+00000000～U+0000FFFF和UCS-2是一样的。</p>
<p>要注意，UCS-2和UCS-4只规定了代码点和文字之间的对应关系，并没有规定代码点在计算机中如何存储。
规定存储方式的称为<strong>UTF</strong>（Unicode Transformation Format），其中应用较多的就是UTF-16和UTF-8了。</p>

<h2 id="content_2_4">UTF-16和UTF-32</h2>

<h3 id="content_2_5">UTF-16</h3>
<p>UTF-16由<a href="http://www.ietf.org/rfc/rfc2781.txt">RFC2781</a>规定，它使用两个字节来表示一个代码点。</p>
<p>不难猜到，UTF-16是完全对应于UCS-2的，即把UCS-2规定的代码点通过Big Endian或Little Endian方式
直接保存下来。UTF-16包括三种：UTF-16，UTF-16BE（Big Endian），UTF-16LE（Little Endian）。</p>
<p>UTF-16BE和UTF-16LE不难理解，而UTF-16就需要通过在文件开头以名为BOM（Byte Order Mark）的字符
来表明文件是Big Endian还是Little Endian。BOM为U+FEFF这个字符。</p>
<p>其实BOM是个小聪明的想法。由于UCS-2没有定义U+FFFE，
因此只要出现 FF FE 或者 FE FF 这样的字节序列，就可以认为它是U+FEFF，
并且可以判断出是Big Endian还是Little Endian。</p>
<p>举个例子。“ABC”这三个字符用各种方式编码后的结果如下：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">UTF-16BE</td><td class="style_td">00 41 00 42 00 43</td></tr><tr><td class="style_td">UTF-16LE</td><td class="style_td">41 00 42 00 43 00</td></tr><tr><td class="style_td">UTF-16(Big Endian)</td><td class="style_td">FE FF 00 41 00 42 00 43</td></tr><tr><td class="style_td">UTF-16(Little Endian)</td><td class="style_td">FF FE 41 00 42 00 43 00</td></tr><tr><td class="style_td">UTF-16(不带BOM)</td><td class="style_td">00 41 00 42 00 43</td></tr></tbody></table></div>
<p>Windows平台下默认的Unicode编码为Little Endian的UTF-16（即上述的 FF FE 41 00 42 00 43 00）。
你可以打开记事本，写上ABC，然后保存，再用二进制编辑器看看它的编码结果。</p>
<div class="img_margin" style="text-align:left"><img src="http://tech.idv2.com/wp-content/uploads/2008/02/notepad-encode.png" alt="notepad-encode.png" title="notepad-encode.png" width="453" height="177" /></div>

<p>另外，UTF-16还能表示一部分的UCS-4代码点——U+10000～U+10FFFF。
表示算法比较复杂，简单说明如下：</p>
<ol class="list1" style="padding-left:16px;margin-left:16px"><li>从代码点U中减去0x10000，得到U'。这样U+10000～U+10FFFF就变成了 0x00000～0xFFFFF。</li>
<li>用20位二进制数表示U'。 U'=yyyyyyyyyyxxxxxxxxxx</li>
<li>将前10位和后10位用W1和W2表示，W1=110110yyyyyyyyyy，W2=110111xxxxxxxxxx，则 W1 = D800～DBFF，W2 = DC00～DFFF。</li></ol>
<p>例如，U+12345表示为 D8 08 DF 45（UTF-16BE），或者08 D8 45 DF（UTF-16LE）。</p>
<p>但是由于这种算法的存在，造成UCS-2中的 U+D800～U+DFFF 变成了无定义的字符。</p>

<h3 id="content_2_6">UTF-32</h3>
<p>UTF-32用四个字节表示代码点，这样就可以完全表示UCS-4的所有代码点，而无需像UTF-16那样使用复杂的算法。
与UTF-16类似，UTF-32也包括UTF-32、UTF-32BE、UTF-32LE三种编码，UTF-32也同样需要BOM字符。
仅用'ABC'举例：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">UTF-32BE</td><td class="style_td">00 00 00 41 00 00 00 42 00 00 00 43</td></tr><tr><td class="style_td">UTF-32LE</td><td class="style_td">41 00 00 00 42 00 00 00 43 00 00 00</td></tr><tr><td class="style_td">UTF-32(Big Endian)</td><td class="style_td">00 00 FE FF 00 00 00 41 00 00 00 42 00 00 00 43</td></tr><tr><td class="style_td">UTF-32(Little Endian)</td><td class="style_td">FF FE 00 00 41 00 00 00 42 00 00 00 43 00 00 00</td></tr><tr><td class="style_td">UTF-32(不带BOM)</td><td class="style_td">00 00 00 41 00 00 00 42 00 00 00 43</td></tr></tbody></table></div>

<h2 id="content_2_7">UTF-8</h2>
<p>UTF-16和UTF-32的一个缺点就是它们固定使用两个或四个字节，
这样在表示纯ASCII文件时会有很多00字节，造成浪费。
而<a href="http://www.rfc.net/rfc3629.html">RFC3629</a>定义的UTF-8则解决了这个问题。</p>
<p>UTF-8用1～4个字节来表示代码点。表示方式如下：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">UCS-2 (UCS-4)</td><td class="style_td">位序列</td><td class="style_td">第一字节</td><td class="style_td">第二字节</td><td class="style_td">第三字节</td><td class="style_td">第四字节</td></tr><tr><td class="style_td">U+0000 .. U+007F</td><td class="style_td">00000000-0xxxxxxx</td><td class="style_td">0xxxxxxx</td><td class="style_td"></td><td class="style_td"></td><td class="style_td"></td></tr><tr><td class="style_td">U+0080 .. U+07FF</td><td class="style_td">00000xxx-xxyyyyyy</td><td class="style_td">110xxxxx</td><td class="style_td">10yyyyyy</td><td class="style_td"></td><td class="style_td"></td></tr><tr><td class="style_td">U+0800 .. U+FFFF</td><td class="style_td">xxxxyyyy-yyzzzzzz</td><td class="style_td">1110xxxx</td><td class="style_td">10yyyyyy</td><td class="style_td">10zzzzzz</td><td class="style_td"></td></tr><tr><td class="style_td">U+10000..U+10FFFF</td><td class="style_td">00000000-000wwwxx-<br />xxxxyyyy-yyzzzzzzz</td><td class="style_td">11110www</td><td class="style_td">10xxxxxx</td><td class="style_td">10yyyyyy</td><td class="style_td">10zzzzzz</td></tr></tbody></table></div>
<p>可见，ASCII字符（U+0000～U+007F）部分完全使用一个字节，避免了存储空间的浪费。
而且UTF-8不再需要BOM字节。</p>
<p>另外，从上表中可以看出，单字节编码的第一字节为[00-7F]，双字节编码的第一字节为[C2-DF]，
三字节编码的第一字节为[E0-EF]。这样只要看到第一个字节的范围就可以知道编码的字节数。
这样也可以大大简化算法。</p>
<!-- end Pukiwiki generated code-->
]]></description>
			<content:encoded><![CDATA[<!-- begin Pukiwiki generated code--><p>最近一直在忙点私活，又好久没写blog了，再不写点的话二月份就又要以单篇文章结束了。
前一阵子一直在研究Unicode，索性把研究结果介绍一下吧。</p>
<p>可能大家都听说过 Unicode、UCS-2、UTF-8 等等词汇，但它们具体是什么意思，
是什么原理，之间有什么关系，恐怕就很少有人明白了。
下面就分别介绍一下它们。</p>
<!-- end Pukiwiki generated code--><span id="more-570"></span><!-- begin Pukiwiki generated code--><hr class="full_hr" />
<div class="contents">
<a id="contents_4"></a>
<ul class="list1" style="padding-left:16px;margin-left:16px"><li><a href="#content_4_0">  基本知识</a>
<ul class="list2" style="padding-left:16px;margin-left:16px"><li><a href="#content_4_1">  字节和字符的区别</a></li>
<li><a href="#content_4_2">  Big Endian和Little Endian</a></li></ul></li>
<li><a href="#content_4_3">  UCS-2和UCS-4</a></li>
<li><a href="#content_4_4">  UTF-16和UTF-32</a>
<ul class="list2" style="padding-left:16px;margin-left:16px"><li><a href="#content_4_5">  UTF-16</a></li>
<li><a href="#content_4_6">  UTF-32</a></li></ul></li>
<li><a href="#content_4_7">  UTF-8</a></li></ul>
</div>

<h2 id="content_4_0">基本知识</h2>
<p>介绍Unicode之前，首先要讲解一些基础知识。虽然跟Unicode没有直接的关系，
但想弄明白Unicode，没这些还真不行。</p>

<h3 id="content_4_1">字节和字符的区别</h3>
<p>咦，字节和字符能有什么区别啊？不都是一样的吗？完全正确，但只是在古老的DOS时代。
当Unicode出现后，字节和字符就不一样了。</p>
<p>字节（octet）是一个八位的存储单元，取值范围一定是0～255。而字符（character，或者word）
为语言意义上的符号，范围就不一定了。例如在UCS-2中定义的字符范围为0～65535，
它的一个字符占用两个字节。</p>

<h3 id="content_4_2">Big Endian和Little Endian</h3>
<p>上面提到了一个字符可能占用多个字节，那么这多个字节在计算机中如何存储呢？
比如字符0xabcd，它的存储格式到底是 AB CD，还是 CD AB 呢？</p>
<p>实际上两者都有可能，并分别有不同的名字。如果存储为 AB CD，则称为<strong>Big Endian</strong>；
如果存储为 CD AB，则称为<strong>Little Endian</strong>。</p>
<p>具体来说，以下这种存储格式为Big Endian，因为值(0xabcd)的高位(0xab)存储在前面：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">地址</td><td class="style_td">值</td></tr><tr><td class="style_td">0x00000000</td><td class="style_td">AB</td></tr><tr><td class="style_td">0x00000001</td><td class="style_td">CD</td></tr></tbody></table></div>
<p>相反，以下这种存储格式为Little Endian：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">地址</td><td class="style_td">值</td></tr><tr><td class="style_td">0x00000000</td><td class="style_td">CD</td></tr><tr><td class="style_td">0x00000001</td><td class="style_td">AB</td></tr></tbody></table></div>

<h2 id="content_4_3">UCS-2和UCS-4</h2>
<p>Unicode是为整合全世界的所有语言文字而诞生的。任何文字在Unicode中都对应一个值，
这个值称为<strong>代码点</strong>（code point）。代码点的值通常写成 U+ABCD 的格式。
而文字和代码点之间的对应关系就是<strong>UCS-2</strong>（Universal Character Set coded in 2 octets）。
顾名思义，UCS-2是用两个字节来表示代码点，其取值范围为 U+0000～U+FFFF。</p>
<p>为了能表示更多的文字，人们又提出了UCS-4，即用四个字节表示代码点。
它的范围为 U+00000000～U+7FFFFFFF，其中 U+00000000～U+0000FFFF和UCS-2是一样的。</p>
<p>要注意，UCS-2和UCS-4只规定了代码点和文字之间的对应关系，并没有规定代码点在计算机中如何存储。
规定存储方式的称为<strong>UTF</strong>（Unicode Transformation Format），其中应用较多的就是UTF-16和UTF-8了。</p>

<h2 id="content_4_4">UTF-16和UTF-32</h2>

<h3 id="content_4_5">UTF-16</h3>
<p>UTF-16由<a href="http://www.ietf.org/rfc/rfc2781.txt">RFC2781</a>规定，它使用两个字节来表示一个代码点。</p>
<p>不难猜到，UTF-16是完全对应于UCS-2的，即把UCS-2规定的代码点通过Big Endian或Little Endian方式
直接保存下来。UTF-16包括三种：UTF-16，UTF-16BE（Big Endian），UTF-16LE（Little Endian）。</p>
<p>UTF-16BE和UTF-16LE不难理解，而UTF-16就需要通过在文件开头以名为BOM（Byte Order Mark）的字符
来表明文件是Big Endian还是Little Endian。BOM为U+FEFF这个字符。</p>
<p>其实BOM是个小聪明的想法。由于UCS-2没有定义U+FFFE，
因此只要出现 FF FE 或者 FE FF 这样的字节序列，就可以认为它是U+FEFF，
并且可以判断出是Big Endian还是Little Endian。</p>
<p>举个例子。“ABC”这三个字符用各种方式编码后的结果如下：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">UTF-16BE</td><td class="style_td">00 41 00 42 00 43</td></tr><tr><td class="style_td">UTF-16LE</td><td class="style_td">41 00 42 00 43 00</td></tr><tr><td class="style_td">UTF-16(Big Endian)</td><td class="style_td">FE FF 00 41 00 42 00 43</td></tr><tr><td class="style_td">UTF-16(Little Endian)</td><td class="style_td">FF FE 41 00 42 00 43 00</td></tr><tr><td class="style_td">UTF-16(不带BOM)</td><td class="style_td">00 41 00 42 00 43</td></tr></tbody></table></div>
<p>Windows平台下默认的Unicode编码为Little Endian的UTF-16（即上述的 FF FE 41 00 42 00 43 00）。
你可以打开记事本，写上ABC，然后保存，再用二进制编辑器看看它的编码结果。</p>
<div class="img_margin" style="text-align:left"><img src="http://tech.idv2.com/wp-content/uploads/2008/02/notepad-encode.png" alt="notepad-encode.png" title="notepad-encode.png" width="453" height="177" /></div>

<p>另外，UTF-16还能表示一部分的UCS-4代码点——U+10000～U+10FFFF。
表示算法比较复杂，简单说明如下：</p>
<ol class="list1" style="padding-left:16px;margin-left:16px"><li>从代码点U中减去0x10000，得到U'。这样U+10000～U+10FFFF就变成了 0x00000～0xFFFFF。</li>
<li>用20位二进制数表示U'。 U'=yyyyyyyyyyxxxxxxxxxx</li>
<li>将前10位和后10位用W1和W2表示，W1=110110yyyyyyyyyy，W2=110111xxxxxxxxxx，则 W1 = D800～DBFF，W2 = DC00～DFFF。</li></ol>
<p>例如，U+12345表示为 D8 08 DF 45（UTF-16BE），或者08 D8 45 DF（UTF-16LE）。</p>
<p>但是由于这种算法的存在，造成UCS-2中的 U+D800～U+DFFF 变成了无定义的字符。</p>

<h3 id="content_4_6">UTF-32</h3>
<p>UTF-32用四个字节表示代码点，这样就可以完全表示UCS-4的所有代码点，而无需像UTF-16那样使用复杂的算法。
与UTF-16类似，UTF-32也包括UTF-32、UTF-32BE、UTF-32LE三种编码，UTF-32也同样需要BOM字符。
仅用'ABC'举例：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">UTF-32BE</td><td class="style_td">00 00 00 41 00 00 00 42 00 00 00 43</td></tr><tr><td class="style_td">UTF-32LE</td><td class="style_td">41 00 00 00 42 00 00 00 43 00 00 00</td></tr><tr><td class="style_td">UTF-32(Big Endian)</td><td class="style_td">00 00 FE FF 00 00 00 41 00 00 00 42 00 00 00 43</td></tr><tr><td class="style_td">UTF-32(Little Endian)</td><td class="style_td">FF FE 00 00 41 00 00 00 42 00 00 00 43 00 00 00</td></tr><tr><td class="style_td">UTF-32(不带BOM)</td><td class="style_td">00 00 00 41 00 00 00 42 00 00 00 43</td></tr></tbody></table></div>

<h2 id="content_4_7">UTF-8</h2>
<p>UTF-16和UTF-32的一个缺点就是它们固定使用两个或四个字节，
这样在表示纯ASCII文件时会有很多00字节，造成浪费。
而<a href="http://www.rfc.net/rfc3629.html">RFC3629</a>定义的UTF-8则解决了这个问题。</p>
<p>UTF-8用1～4个字节来表示代码点。表示方式如下：</p>
<div class="ie5"><table class="style_table" cellspacing="1" border="0"><tbody><tr><td class="style_td">UCS-2 (UCS-4)</td><td class="style_td">位序列</td><td class="style_td">第一字节</td><td class="style_td">第二字节</td><td class="style_td">第三字节</td><td class="style_td">第四字节</td></tr><tr><td class="style_td">U+0000 .. U+007F</td><td class="style_td">00000000-0xxxxxxx</td><td class="style_td">0xxxxxxx</td><td class="style_td"></td><td class="style_td"></td><td class="style_td"></td></tr><tr><td class="style_td">U+0080 .. U+07FF</td><td class="style_td">00000xxx-xxyyyyyy</td><td class="style_td">110xxxxx</td><td class="style_td">10yyyyyy</td><td class="style_td"></td><td class="style_td"></td></tr><tr><td class="style_td">U+0800 .. U+FFFF</td><td class="style_td">xxxxyyyy-yyzzzzzz</td><td class="style_td">1110xxxx</td><td class="style_td">10yyyyyy</td><td class="style_td">10zzzzzz</td><td class="style_td"></td></tr><tr><td class="style_td">U+10000..U+10FFFF</td><td class="style_td">00000000-000wwwxx-<br />xxxxyyyy-yyzzzzzzz</td><td class="style_td">11110www</td><td class="style_td">10xxxxxx</td><td class="style_td">10yyyyyy</td><td class="style_td">10zzzzzz</td></tr></tbody></table></div>
<p>可见，ASCII字符（U+0000～U+007F）部分完全使用一个字节，避免了存储空间的浪费。
而且UTF-8不再需要BOM字节。</p>
<p>另外，从上表中可以看出，单字节编码的第一字节为[00-7F]，双字节编码的第一字节为[C2-DF]，
三字节编码的第一字节为[E0-EF]。这样只要看到第一个字节的范围就可以知道编码的字节数。
这样也可以大大简化算法。</p>
<!-- end Pukiwiki generated code-->
]]></content:encoded>
			<wfw:commentRss>http://tech.idv2.com/2008/02/21/unicode-intro/feed/</wfw:commentRss>
		<slash:comments>37</slash:comments>
		</item>
	</channel>
</rss>
