<?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>简单生活 —— Kevin Yang的博客 &#187; 技术随笔</title> <atom:link href="http://www.imkevinyang.com/categories/techarticles/feed" rel="self" type="application/rss+xml" /><link>http://www.imkevinyang.com</link> <description>It&#039;s all about sharing</description> <lastBuildDate>Thu, 29 Jul 2010 17:50:43 +0000</lastBuildDate> <generator>http://wordpress.org/?v=2.9.1</generator> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <item><title>几个有趣的Javascript Hack</title><link>http://www.imkevinyang.com/2010/07/%e5%87%a0%e4%b8%aa%e6%9c%89%e8%b6%a3%e7%9a%84javascript-hack.html</link> <comments>http://www.imkevinyang.com/2010/07/%e5%87%a0%e4%b8%aa%e6%9c%89%e8%b6%a3%e7%9a%84javascript-hack.html#comments</comments> <pubDate>Fri, 23 Jul 2010 12:06:48 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[Web传统技术]]></category> <category><![CDATA[hack]]></category> <category><![CDATA[Javascript]]></category> <category><![CDATA[显示密码]]></category> <category><![CDATA[编辑网页]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/07/%e5%87%a0%e4%b8%aa%e6%9c%89%e8%b6%a3%e7%9a%84javascript-hack.html</guid> <description><![CDATA[<p>在网上看到几个有意思的Javascript代码，和大家分享一下。</p><p style="font-weight: bold">1. 直接在浏览器中编辑网页内容</p><pre class="brush: js">javascript:document.body.contentEditable='true';void(0);</pre><p><strong></strong></p><p>访问任意网站，在地址栏输入以上代码，会发生当前网页已经变成编辑模式了。将上述代码中的true改成false重新执行一遍即可恢复。</p><p><img style="display: inline" title="在浏览器中编辑网页内容" alt="在浏览器中编辑网页内容" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image14.png" width="581" height="195" /></p><p>&#160;</p><p style="font-weight: bold">2. 舞动的图片</p><pre class="brush: js">javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.2&#8230;</pre>]]></description> <content:encoded><![CDATA[<p>在网上看到几个有意思的Javascript代码，和大家分享一下。</p><p style="font-weight: bold">1. 直接在浏览器中编辑网页内容</p><pre class="brush: js">javascript:document.body.contentEditable='true';void(0);</pre><p><strong></strong></p><p>访问任意网站，在地址栏输入以上代码，会发生当前网页已经变成编辑模式了。将上述代码中的true改成false重新执行一遍即可恢复。</p><p><img style="display: inline" title="在浏览器中编辑网页内容" alt="在浏览器中编辑网页内容" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image14.png" width="581" height="195" /></p><p>&#160;</p><p style="font-weight: bold">2. 舞动的图片</p><pre class="brush: js">javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.images; DIL=DI.length; function A(){for(i=0; i-DIL; i++){DIS=DI[ i ].style; DIS.position='absolute'; DIS.left=(Math.sin(R*x1+i*x2+x3)*x4+x5)+&quot;px&quot;; DIS.top=(Math.cos(R*y1+i*y2+y3)*y4+y5)+&quot;px&quot;}R++}setInterval('A()',5); void(0);</pre><p>这段js代码可能很多人已经见识过了。他的作用就是让网页上的图片飞舞起来。</p><p><img style="display: inline" title="舞动的图片" alt="舞动的图片" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image15.png" width="542" height="215" /></p><p>&#160;</p><p style="font-weight: bold">3. 密码框密文变明文</p><pre class="brush: js">javascript:(function(){var s,F,j,f,i;s=&quot;&quot;;F=document.forms;for(j=0;j&lt;F.length;++j){f=F[j];for(i=0;i&lt;f.length;++i){if(f[i].type.toLowerCase()==&quot;password&quot;)s+=f[i].value+&quot;\n&quot;;}}if(s)alert(&quot;Passwords in forms on this page:\n\n&quot;+s);else alert(&quot;There are no passwords in forms on this page.&quot;);})();</pre><p>访问一个带密码框的页面，然后地址栏输入以上代码，就会弹出当前密码框中的密码了，别干坏事哟~</p><p><img style="display: inline" title="密码框密文变明文" alt="密码框密文变明文" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image16.png" width="302" height="288" /></p><p style="text-align: right">——<a title="几个有趣的Javascript Hack" href="http://www.imkevinyang.com/2010/07/%e5%87%a0%e4%b8%aa%e6%9c%89%e8%b6%a3%e7%9a%84Javascript%20Hack.html"><em>Kevin Yang</em></a></p>标签：<a href="http://www.imkevinyang.com/tags/hack" title="hack" rel="tag">hack</a>, <a href="http://www.imkevinyang.com/tags/javascript" title="Javascript" rel="tag">Javascript</a>, <a href="http://www.imkevinyang.com/categories/techarticles/web%e4%bc%a0%e7%bb%9f%e6%8a%80%e6%9c%af" title="Web传统技术" rel="tag">Web传统技术</a>, <a href="http://www.imkevinyang.com/tags/%e6%98%be%e7%a4%ba%e5%af%86%e7%a0%81" title="显示密码" rel="tag">显示密码</a>, <a href="http://www.imkevinyang.com/tags/%e7%bc%96%e8%be%91%e7%bd%91%e9%a1%b5" title="编辑网页" rel="tag">编辑网页</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2009/11/button%e6%a0%87%e7%ad%be%e9%bc%a0%e6%a0%87%e7%82%b9%e5%87%bb%e4%ba%8b%e4%bb%b6%e7%9a%84%e8%a7%a6%e5%8f%91%e6%ba%90%e9%97%ae%e9%a2%98.html" title="Button标签鼠标点击事件的触发源问题 (2009/11/27)">Button标签鼠标点击事件的触发源问题</a> (2009/11/27)</li><li><a href="http://www.imkevinyang.com/2009/03/ie%e4%b8%ad%e4%bd%bf%e7%94%a8windowopen%e6%89%93%e5%bc%80%e6%96%b0%e7%aa%97%e5%8f%a3%e6%97%b6%e6%97%a0%e6%b3%95%e8%8e%b7%e5%8f%96referrer%e5%af%b9%e8%b1%a1.html" title="IE中使用window.open打开新窗口时无法获取Referrer对象 (2009/03/07)">IE中使用window.open打开新窗口时无法获取Referrer对象</a> (2009/03/07)</li><li><a href="http://www.imkevinyang.com/2009/07/javascript-%e4%b8%ad%e7%9a%84false%e9%9b%b6%e5%80%bcnullundefined%e5%92%8c%e7%a9%ba%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%af%b9%e8%b1%a1.html" title="Javascript 中的false,零值,null,undefined和空字符串对象 (2009/07/07)">Javascript 中的false,零值,null,undefined和空字符串对象</a> (2009/07/07)</li><li><a href="http://www.imkevinyang.com/2009/05/javascript%e4%b8%ad%e8%8e%b7%e5%8f%96%e5%87%ba%e9%94%99%e4%bb%a3%e7%a0%81%e6%89%80%e5%9c%a8%e6%96%87%e4%bb%b6%e5%8f%8a%e8%a1%8c%e6%95%b0.html" title="Javascript中获取出错代码所在文件及行数 (2009/05/18)">Javascript中获取出错代码所在文件及行数</a> (2009/05/18)</li><li><a href="http://www.imkevinyang.com/2009/04/javascript%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%93%88%e5%b8%8c%e5%87%bd%e6%95%b0.html" title="Javascript字符串哈希函数 (2009/04/11)">Javascript字符串哈希函数</a> (2009/04/11)</li><li><a href="http://www.imkevinyang.com/2009/06/javascript%e6%93%8d%e7%ba%b5cookie.html" title="Javascript操纵Cookie (2009/06/11)">Javascript操纵Cookie</a> (2009/06/11)</li><li><a href="http://www.imkevinyang.com/2010/05/%e4%b8%ba%e4%bb%80%e4%b9%88iis77-5%e7%9a%84gzip%e4%b8%8d%e8%b5%b7%e4%bd%9c%e7%94%a8.html" title="为什么IIS7/7.5的Gzip不起作用 (2010/05/08)">为什么IIS7/7.5的Gzip不起作用</a> (2010/05/08)</li><li><a href="http://www.imkevinyang.com/2009/09/%e4%bd%bf%e7%94%a8%e7%9b%b8%e5%af%b9url%e6%97%a0%e7%bc%9d%e5%88%87%e6%8d%a2http-https.html" title="使用相对Url无缝切换HTTP-HTTPS (2009/09/18)">使用相对Url无缝切换HTTP-HTTPS</a> (2009/09/18)</li><li><a href="http://www.imkevinyang.com/2010/02/%e5%bd%93%e5%89%8d%e6%97%a5%e6%9c%9f110%e5%b9%b4.html" title="当前日期110年 (2010/02/12)">当前日期110年</a> (2010/02/12)</li><li><a href="http://www.imkevinyang.com/2009/03/%e6%9c%80%e7%b2%be%e7%ae%80%e7%9a%84%e5%88%a4%e5%88%abie%e7%9a%84javascript%e4%bb%a3%e7%a0%81.html" title="最精简的判别IE的Javascript代码 (2009/03/23)">最精简的判别IE的Javascript代码</a> (2009/03/23)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/07/%e5%87%a0%e4%b8%aa%e6%9c%89%e8%b6%a3%e7%9a%84javascript-hack.html/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Hilo: Windows 7下C++应用程序开发实战演练</title><link>http://www.imkevinyang.com/2010/07/hilo-windows-7%e4%b8%8bc%e5%ba%94%e7%94%a8%e7%a8%8b%e5%ba%8f%e5%bc%80%e5%8f%91%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83.html</link> <comments>http://www.imkevinyang.com/2010/07/hilo-windows-7%e4%b8%8bc%e5%ba%94%e7%94%a8%e7%a8%8b%e5%ba%8f%e5%bc%80%e5%8f%91%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83.html#comments</comments> <pubDate>Tue, 20 Jul 2010 17:58:00 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[其他随笔]]></category> <category><![CDATA[C++]]></category> <category><![CDATA[Hilo]]></category> <category><![CDATA[Windows 7]]></category> <category><![CDATA[开发]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/07/hilo-windows-7%e4%b8%8bc%e5%ba%94%e7%94%a8%e7%a8%8b%e5%ba%8f%e5%bc%80%e5%8f%91%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83.html</guid> <description><![CDATA[<div class="statement"><p> 英文原文链接：<a title="Hilo: Windows 7 C++ Development Walkthroughs" href="http://msdn.microsoft.com/en-us/windows/ff686707.aspx" target="_blank">Hilo: Windows 7 C++ Development Walkthroughs</a></p></div><p>&#34;Hilo&#34;由一系列文章以及示例代码组成的，它向你展示了如何释放Windows 7、Visual Studio 2010还有Visual C++的强大力量，来构建高性能，高响应的富客户端程序。Hilo提供了源代码以及编写指导，帮助你设计和开发令人瞩目的触摸式Windows应用程序。</p><p>在接下来的几周时间里，我们将会在MSDN上发布一系列文&#8230;</p>]]></description> <content:encoded><![CDATA[<div class="statement"><p> 英文原文链接：<a title="Hilo: Windows 7 C++ Development Walkthroughs" href="http://msdn.microsoft.com/en-us/windows/ff686707.aspx" target="_blank">Hilo: Windows 7 C++ Development Walkthroughs</a></div><p>&quot;Hilo&quot;由一系列文章以及示例代码组成的，它向你展示了如何释放Windows 7、Visual Studio 2010还有Visual C++的强大力量，来构建高性能，高响应的富客户端程序。Hilo提供了源代码以及编写指导，帮助你设计和开发令人瞩目的触摸式Windows应用程序。</p><p>在接下来的几周时间里，我们将会在MSDN上发布一系列文章，讲述如何设计和实现一个可以浏览、检索和处理图像照片的触摸式Windows应用程序。这些文章将会涵盖Windows 7的核心技术，描述了如何将这些技术融合到一起来创建一个非常棒的用户体验，并且也会对应用程序的设计和实现等细节做详细的阐述。你可以在这个<a href="http://msdn.microsoft.com/library/ff708696.aspx" target="_blank">这里</a>找到此系列的第一篇文章。它对 Hilo做了一个概述性的介绍，并且说明了此系列中的这些文章和示例程序的所要达到的目标。</p><p><a href="http://www.imkevinyang.com/wp-content/uploads/2010/07/clip_image001.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image001" border="0" alt="clip_image001" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/clip_image001_thumb.jpg" width="244" height="181" /></a></p><p>我们接下来会逐步放出Hilo的相关源代码。第一次Hilo的发布包含了一个Hilo浏览器程序的源代码。这个程序实现了一种创新的旋转木马式的导航界面。这是触摸式的，你可以使用触摸手势快速的浏览和检索。你可以在<a href="http://code.msdn.microsoft.com/Hilo">这里</a>下载到此程序的源代码。</p><p><a href="http://www.imkevinyang.com/wp-content/uploads/2010/07/clip_image002.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/clip_image002_thumb.jpg" width="244" height="176" /></a></p><p>Hilo文章系列，包括与此相关的源代码，是用来让你快速上手一些技术的开发，并且向你展示了如何在自己的应用程序中利用到Windows核心技术的优势。</p><p>如何开始</p><p>1. 为了编译和运行Hilo浏览器程序，你需要安装Visual Studio 2010以及Visual C++。你可以在这里下载到免费的Visual C++ 2010 Express版本。</p><p>2. 安装完Visual C++ 2010 Express之后，解压缩Hilo浏览器程序的源代码，双击打开Hilo.sln解决方案文件。</p><p>3. 在编译菜单中选择重新编译整个Hilo浏览器程序。</p><p>4. 按F5在调试模式下运行此程序。</p>标签：<a href="http://www.imkevinyang.com/tags/c" title="C++" rel="tag">C++</a>, <a href="http://www.imkevinyang.com/tags/hilo" title="Hilo" rel="tag">Hilo</a>, <a href="http://www.imkevinyang.com/tags/windows-7" title="Windows 7" rel="tag">Windows 7</a>, <a href="http://www.imkevinyang.com/categories/techarticles/othertecharticles" title="其他随笔" rel="tag">其他随笔</a>, <a href="http://www.imkevinyang.com/tags/%e5%bc%80%e5%8f%91" title="开发" rel="tag">开发</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2009/02/c%e6%89%ab%e7%9b%b2%e7%b3%bb%e5%88%97%e4%b9%8b-%e6%8c%87%e9%92%88%e4%b8%93%e9%a2%98.html" title="c++扫盲系列之&#8211;指针专题 (2009/02/28)">c++扫盲系列之&#8211;指针专题</a> (2009/02/28)</li><li><a href="http://www.imkevinyang.com/2010/03/g1%e6%89%8b%e6%9c%ba%e4%b8%8e%e7%ac%94%e8%ae%b0%e6%9c%ac%e5%ae%9e%e7%8e%b0wifi%e4%ba%92%e8%81%94%e5%85%b1%e4%ba%ab%e4%b8%8a%e7%bd%91.html" title="G1手机与笔记本实现Wifi互联共享上网 (2010/03/02)">G1手机与笔记本实现Wifi互联共享上网</a> (2010/03/02)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/07/hilo-windows-7%e4%b8%8bc%e5%ba%94%e7%94%a8%e7%a8%8b%e5%ba%8f%e5%bc%80%e5%8f%91%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83.html/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>晒晒自己电脑里的常用工具</title><link>http://www.imkevinyang.com/2010/07/%e6%99%92%e6%99%92%e8%87%aa%e5%b7%b1%e7%94%b5%e8%84%91%e9%87%8c%e7%9a%84%e5%b8%b8%e7%94%a8%e5%b7%a5%e5%85%b7.html</link> <comments>http://www.imkevinyang.com/2010/07/%e6%99%92%e6%99%92%e8%87%aa%e5%b7%b1%e7%94%b5%e8%84%91%e9%87%8c%e7%9a%84%e5%b8%b8%e7%94%a8%e5%b7%a5%e5%85%b7.html#comments</comments> <pubDate>Mon, 19 Jul 2010 18:53:26 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[实用工具]]></category> <category><![CDATA[Everything]]></category> <category><![CDATA[Google Reader]]></category> <category><![CDATA[Launchy]]></category> <category><![CDATA[Lingoes]]></category> <category><![CDATA[工具]]></category> <category><![CDATA[常用工具]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/07/%e6%99%92%e6%99%92%e8%87%aa%e5%b7%b1%e7%94%b5%e8%84%91%e9%87%8c%e7%9a%84%e5%b8%b8%e7%94%a8%e5%b7%a5%e5%85%b7.html</guid> <description><![CDATA[<p>前阵子在<a href="http://www.cnblogs.com/kyo-yo/">顾磊</a>的博客上看到他分享自己电脑上辅助软件的文章，觉得不错，于是决定也来晒一晒自己电脑里头的常用工具，分享促进进步，如果你也是个工具控的话，欢迎你也来晒晒。</p><h2>Launchy——快速启动程序</h2><p>两年前一个同事向我推荐了这款软件，一直受用至今。</p><p><img style="margin: 0px; display: inline" class="wlDisabledImage" title="image[3]" alt="image[3]" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image31.png" width="300" height="126" /></p><p>每天我都会无数次的按下Alt+空格，然后输入我要启动的程序的名字（大部分情况下我只需要键入程序的前两个字母即可），直接就打开我要的程序了。不用像以前在堆满图标的桌面上或者在臃肿的开始菜单中定位该程序了&#8230;</p>]]></description> <content:encoded><![CDATA[<p>前阵子在<a href="http://www.cnblogs.com/kyo-yo/">顾磊</a>的博客上看到他分享自己电脑上辅助软件的文章，觉得不错，于是决定也来晒一晒自己电脑里头的常用工具，分享促进进步，如果你也是个工具控的话，欢迎你也来晒晒。</p><h2>Launchy——快速启动程序</h2><p>两年前一个同事向我推荐了这款软件，一直受用至今。</p><p><img style="margin: 0px; display: inline" class="wlDisabledImage" title="image[3]" alt="image[3]" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image31.png" width="300" height="126" /></p><p>每天我都会无数次的按下Alt+空格，然后输入我要启动的程序的名字（大部分情况下我只需要键入程序的前两个字母即可），直接就打开我要的程序了。不用像以前在堆满图标的桌面上或者在臃肿的开始菜单中定位该程序了。</p><p>Launchy还支持插件机制，通过插件你可以实现快速搜索，快速计算等。不过这些功能我很少用到。</p><p><strong>使用小技巧：</strong></p><p>Launchy默认会检索开始菜单还有桌面等常用位置，于是当你键入程序名的时候可能会出来太多干扰信息，而且有时候程序的名字比较长，不好记。我个人喜欢建立一个专门用于存放快捷方式的目录，让Launchy只检索这个目录。然后为每个程序起一个好记的名字。</p><h2>Everything——没有比他搜索得更快的了</h2><p>以前我也写过文章（<a href="http://www.imkevinyang.com/2009/07/%e5%8f%b2%e4%b8%8a%e6%9c%80%e5%bf%ab%e7%9a%84%e6%90%9c%e7%b4%a2%e5%b7%a5%e5%85%b7everything.html">【推荐】史上最快的搜索工具——Everything</a>）推荐过这款工具。自从有了这个工具之后，我就很少打开资源管理器来查找资料了。一个Alt+G调出搜索窗口，然后输入要查找的资料的关键字，以空格分开，例如我想查找C#相关的电子书，那么我可以输入“csharp chm|pdf”，半秒不用的时间就可以列出所有符合条件的材料了。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image7.png" width="499" height="250" /></p><p>Everything还支持正则表达式，并且可以集成到右键菜单，实现对某个特定文件夹的检索，内置一个简单的Web服务器，允许其他人通过浏览器访问Everything进行检索。功能非常强大，而占用空间非常小，120G硬盘，索引也就几M而已。唯一的不足就是只能检索文件名和路径，不支持内容检索。不过话说回来，我们一天能有多少次用到内容检索呢？即使需要，我们也会到具体的软件里头去搜索。各司其责嘛。</p><h2>Lingoes——小巧而强大的免费词典工具</h2><p>相信很多人都曾用过金山词霸这类词典软件，体积庞大不说，还收费（现在是不是免费我就不知道了）。Lingoes是国人开发的一款非常小巧，但是功能非常完整好用的词典软件。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image8.png" width="392" height="194" />&#160;</p><p>它加上一些基本词典总体积也是十几M而已，算是非常小巧了。但是功能可不逊，屏幕取词，屏幕阅读等功能一点都不少，而且现有的词典质量也非常高，如果你硬盘大的话，你可以下个维基百科还有韦氏词典（需要几百M的空间）。而且他的界面也非常美观，让人用起来非常舒服。</p><h2>Foxit Reader——Acrobat Reader的完美替代品</h2><p>很多人都用过Adobe的Acrobat PDF阅读器。但是那个阅读器体积实在过于庞大，启动也很慢。Foxit Reader一面世就受到很多人的喜欢。因为他体积小，最简安装只需要3M。但功能一点都不含糊。最近Foxit Software公司又决定把原先收费版的Foxit Reader Pro给免费了，赢得了不错口碑。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image9.png" width="159" height="154" /></p><p>Foxit Reader的渲染效果上次看到有人评测过，基本和Adobe的官方PDF阅读器不相上下。我个人的阅读感受也挺不错的。没有什么不舒服。功能上，因为我不怎么用过Adobe的那个阅读器，所以不知道是不是很全，反正我需要的都有了，包括文档目录，书签，注释，文字拷贝等。</p><h2>StrokeIt——把鼠标变成程序的指挥棒</h2><p>鼠标手势在很多浏览器中都非常流行，因为它非常直观方便，也特别适合懒人。StrokeIt把鼠标手势带到了Windows系统。 StrokeIt预定义的鼠标手势非常直观。按下右键向左拖动表示后退，向右表示前进，向右上方表示最大化窗口，左下方表示最小化。当你用着StrokeIt的时候，你会感觉自己像一个指挥家，挥动着指挥棒来指使你的电脑程序干活。感觉非常不错。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image10.png" width="425" height="266" /></p><p>StrokeIt很强大，允许你为任意程序定制适合自己的手势命令。</p><h2>Windows Live Writer——博客离线编辑器中的佼佼者</h2><p>写博的人一般不喜欢在博客后台直接编辑文章，这是出于很多考虑。一个是容易因为网络问题造成文章修改丢失，还有一个是无法方便的上传图片，样式编辑不方便等等。离线编辑器解决了这些问题。而微软推出的Live Writer则是博客离线编辑工具中编辑功能最为强大的，而且还是免费的。通过插件还可以扩展其功能，例如我经常用到的Dynamic Template插件，允许你往页面中插入模板化的文本，非常强大，可以参考我以前写的文章<a href="http://www.imkevinyang.com/2009/04/dynamic-template%e9%9d%9e%e5%b8%b8%e5%a5%bd%e7%94%a8%e7%9a%84windows-live-writer%e5%8a%a8%e6%80%81%e6%a8%a1%e6%9d%bf%e6%8f%92%e4%bb%b6.html">Dynamic Template——非常好用的Windows Live Writer动态模板插件</a>。</p><p>还有两款博客离线编辑器ScribeFire以及Zoundry Raven也很有特点，其中ScribeFire是以浏览器插件的形式存在的，支持Firefox、Chrome以及Safari，允许使用css的方式来格式化文本而不像Writer一样通过Font元素来格式化文本，而且也支持直接粘贴图片；而Zoundry的文章管理功能很强大，这正是LiveWriter所欠缺的。但是这两者的编辑体验都不及Live Writer那样，真正做到所见即所得。这也是我为什么喜欢用Writer写博客的缘故。</p><h2>OneNote——让我爱不释手的笔记本软件</h2><p>当我第一次用上OneNote的时候，我就深深爱上了她——很抱歉，我用了这么肉麻的词，让你起鸡皮疙瘩了是我的过错。</p><p>在OneNote之前，我一直在寻找一款适合自己的知识管理软件，尝试过很多网上评价不错的软件，一般用不到两天就被我抛弃了，大多数是因为界面太丑陋，编辑功能太简陋或者不直观导致我没有使用的欲望。有一次看到同事在用OneNote做会议记录，我看着不错就安装了一个试试。体验完只有一个感受，“就是她了”。从此个人的知识管理，包括日记都是使用OneNote来整理了。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image11.png" width="494" height="295" /></p><p>OneNote借鉴了我们传统的纸质笔记本的理念，把资料管理分为三个级别，笔记本&gt;分区&gt;页面。和其他电子笔记本软件不同的是，OneNote页面就像一块大画布，你可以在任意位置输入任何信息，包括文本信息，音视频信息，或者随处粘贴任何图片，绘制任何图形。如果你使用的是平板电脑的话那就更便捷了，你可以像在纸上一样涂鸦。这种整理资料的方式极大的释放了人们的想象力。加上不错的搜索功能，东西再多也不乱了。她还有一个对于办公一族非常实用的功能，就是实时会议共享。几个人坐在一块，只要有无线网就可以直接通过OneNote共享笔记了，而无须通过额外的服务器。可惜这个功能在OneNote2010中出于性能以及安全性考虑而被取消了，非常遗憾。</p><h2>Firefox——浏览器中的瑞士军刀</h2><p>作为Web开发人员，我电脑里头安装了N多种浏览器，IE8、Maxthon、Firefox、Chrome、Opera、Safari等等。每一种浏览器我都用过很长一段时间，其中Firefox是我个人觉得功能最为强大的，但是他的强大不在于他自身，而在于其背后庞大的插件库。这是任何浏览器，包括现在骨粉们所推崇的Chrome浏览器，都比不上的，至少暂时比不上。基本上我能想象到的和不能想象到的需求，都能在插件库中找到满足要求的（有点夸张喔~~）。所以说Firefox是一把瑞士军刀一点都不过分。</p><h2>Fiddler——Web分析强力助手</h2><p>做Web分析经常需要分析HTTP消息。Fiddler是微软推出的一款小而强的免费HTTP分析工具，关于它的介绍，我在以前的文章（<a href="http://www.imkevinyang.com/2009/11/%E3%80%90%E6%8E%A8%E8%8D%90%E3%80%91%E4%B8%A4%E6%AC%BEhttp%E6%B5%81%E9%87%8F%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7%E7%9A%84%E6%AF%94%E8%BE%83.html">【推荐】两款HTTP流量分析工具的比较</a>）中也有提到。他的功能基本可以满足各种需求，如果不满足，你甚至可以自己编写简单的插件来实现。</p><h2>Expresso——正则表达式工作室</h2><p><a href="http://www.ultrapico.com/">Ultrapico Expresso</a>是我工作中经常使用的一个非常强大的正则表达式构建、测试以及代码生成工具。它能够对你构建的正则表达式进行解析、验证，并输出解析结果，提供性能测试工具，支持C#、VB等代码生成，最重要的是，他提供了一个非常方便强大的正则表达式语法构建面板，即使你对正则表达式生疏了，也能在它的帮助下构建出你想要的正则模式来。而且，它还免费获取授权码，无需费心找破解了。软件里自带的帮助文档以及正则表达式库可以帮助你快速从正则菜鸟升级为正则高手。</p><h2>Notepad2——你可以把Windows自带的记事本给扔了</h2><p>很多人都不喜欢用Windows自带的记事本工具，原因是因为他功能过于简单，并且不支持大容量文本。我也从来不用。电脑里头有很多替代品，例如Notepad2，Notepad++以及UltraEdit32。这三款软件我都用过很长一段时间，UltraEdit32是处理大容量文本性能最强大的一款，功能和Notepad++类似，也是非常全面强大。但强大也带来臃肿，这两款软件的打开速度让我不是很满意。因此我习惯于使用Notepad2来作为日常的文本编辑器。他的功能在大多数情况下已经够我用了。包括设置文件编码，代码语法着色，正则查找等。而且对大容量文本支持也还行。至少和Notepad++是一样的。</p><h2>Google Reader——让知识随时随地分享</h2><p>看到这个标题你可能会一愣，因为首先Google Reader和上面提到的各式工具似乎不是一个种族的，再者，Google Reader不是一个RSS阅读器么，和知识分享有什么关系？</p><p>事实上，Google Reader已经成为我生活中不可或缺的一个获取知识、分享知识的工具了。</p><p>Google Reader和很多RSS阅读器不一样的是，他会保存RSS的历史内容。例如我才订阅了某个人的博客，这个时候从技术上来说，我只能看到该RSS的最新内容，但是如果这个RSS以前已经有人订阅过了，那么我就可以看到以前的历史内容。Google Reader只要收到有人订阅，就开始将数据保存下来。而且在Google Reader中，你可以随时和别人分享你阅读过的信息，而不必局限于Google Reader里头的内容。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image12.png" width="245" height="152" /></p><p>你只要把上面这个“Note in Reader”拖放到书签栏上，以后当你在网上看到任何有意思的文章，你就可以选中一段文本，然后点击这个书签，就可以将该文章分享给你的Google Reader好友。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image13.png" width="367" height="216" /></p><p>现在在互联网上获取信息是越来越容易了，但是信息的质量也在降低，如何从庞大的信息源中取出那些对自己有用的信息不是一件轻松事。查看别人分享的内容，可以让我们快速发现一些质量比较高的知识。同时，为了回馈，我们也需要把自己认为好的文章分享出来。每个人关注的兴趣面是不同的，这也可以从每个好友分享的内容看出来。有些人分享纯粹技术的很多，有些人喜欢分享一些有趣的新闻，有些人喜欢分享思维学习的一些理论。通过经常阅读好友分享的内容，你会对这些好友的兴趣有更多的了解，从而就能更有选择性的阅读信息。</p><p>我的Google Reader页面是<a title="http://www.google.com/profiles/114279556099966673203" href="http://www.google.com/profiles/114279556099966673203">http://www.google.com/profiles/114279556099966673203</a>，大家可以在Buzz上Follow me，这样就可以在Reader中看到我分享的文章了。</p><p style="text-align: right">——<a title="晒晒自己电脑里的常用工具" href="http://www.imkevinyang.com/2010/07/%e6%99%92%e6%99%92%e8%87%aa%e5%b7%b1%e7%94%b5%e8%84%91%e9%87%8c%e7%9a%84%e5%b8%b8%e7%94%a8%e5%b7%a5%e5%85%b7.html"><em>Kevin Yang</em></a></p>标签：<a href="http://www.imkevinyang.com/tags/everything" title="Everything" rel="tag">Everything</a>, <a href="http://www.imkevinyang.com/tags/google-reader" title="Google Reader" rel="tag">Google Reader</a>, <a href="http://www.imkevinyang.com/tags/launchy" title="Launchy" rel="tag">Launchy</a>, <a href="http://www.imkevinyang.com/tags/lingoes" title="Lingoes" rel="tag">Lingoes</a>, <a href="http://www.imkevinyang.com/categories/techarticles/usefultools" title="实用工具" rel="tag">实用工具</a>, <a href="http://www.imkevinyang.com/tags/%e5%b7%a5%e5%85%b7" title="工具" rel="tag">工具</a>, <a href="http://www.imkevinyang.com/tags/%e5%b8%b8%e7%94%a8%e5%b7%a5%e5%85%b7" title="常用工具" rel="tag">常用工具</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2009/11/%e3%80%90%e6%8e%a8%e8%8d%90%e3%80%91%e4%b8%a4%e6%ac%behttp%e6%b5%81%e9%87%8f%e5%88%86%e6%9e%90%e5%b7%a5%e5%85%b7%e7%9a%84%e6%af%94%e8%be%83.html" title="【推荐】两款HTTP流量分析工具的比较 (2009/11/08)">【推荐】两款HTTP流量分析工具的比较</a> (2009/11/08)</li><li><a href="http://www.imkevinyang.com/2009/07/%e5%8f%b2%e4%b8%8a%e6%9c%80%e5%bf%ab%e7%9a%84%e6%90%9c%e7%b4%a2%e5%b7%a5%e5%85%b7everything.html" title="【推荐】史上最快的搜索工具&mdash;&mdash;Everything (2009/07/21)">【推荐】史上最快的搜索工具&mdash;&mdash;Everything</a> (2009/07/21)</li><li><a href="http://www.imkevinyang.com/2009/03/%e5%85%8d%e8%b4%b9%e7%9a%84seo%e6%a3%80%e6%b5%8b%e5%b7%a5%e5%85%b7.html" title="免费的SEO检测工具 (2009/03/22)">免费的SEO检测工具</a> (2009/03/22)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/07/%e6%99%92%e6%99%92%e8%87%aa%e5%b7%b1%e7%94%b5%e8%84%91%e9%87%8c%e7%9a%84%e5%b8%b8%e7%94%a8%e5%b7%a5%e5%85%b7.html/feed</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>SSAS处理时&#8220;找不到属性键&#8221;的解决办法</title><link>http://www.imkevinyang.com/2010/07/ssas%e5%a4%84%e7%90%86%e6%97%b6%e6%89%be%e4%b8%8d%e5%88%b0%e5%b1%9e%e6%80%a7%e9%94%ae%e7%9a%84%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95.html</link> <comments>http://www.imkevinyang.com/2010/07/ssas%e5%a4%84%e7%90%86%e6%97%b6%e6%89%be%e4%b8%8d%e5%88%b0%e5%b1%9e%e6%80%a7%e9%94%ae%e7%9a%84%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95.html#comments</comments> <pubDate>Sun, 18 Jul 2010 17:07:20 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[BI/数据库]]></category> <category><![CDATA[Analysis Services]]></category> <category><![CDATA[SSAS]]></category> <category><![CDATA[处理异常]]></category> <category><![CDATA[属性键]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/07/ssas%e5%a4%84%e7%90%86%e6%97%b6%e6%89%be%e4%b8%8d%e5%88%b0%e5%b1%9e%e6%80%a7%e9%94%ae%e7%9a%84%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95.html</guid> <description><![CDATA[<p>在SSAS中，经常会遇到“Attribute key not found（找不到属性键）”的错误，这种错误通常是由于某个维度属性（Dimension Attribute）的<font face="Aparajita">数</font>据没能从Sql Server导入到Analysis Services中(处理这个维度属性的过程倒是不会出错)，而维度关键属性（Dimension Key Attribute）处理时又需要这些数据，因而引发了这个“找不到属性键”的错误。这么讲可能有些抽象，你可以看我以前写的文章<a href="http://www.imkevinyang.com/2009/10/%E5%85%A8%E5%8D%8A%E8%A7%92%E7%A9%BA%E6%A0%BC%E5%AF%BC%E8%87%B4%E7%9A%84analysis-service%E5%A4%84%E7%90%86%E9%94%99%E8%AF%AF.html">全半角&#8230;</a></p>]]></description> <content:encoded><![CDATA[<p>在SSAS中，经常会遇到“Attribute key not found（找不到属性键）”的错误，这种错误通常是由于某个维度属性（Dimension Attribute）的<font face="Aparajita">数</font>据没能从Sql Server导入到Analysis Services中(处理这个维度属性的过程倒是不会出错)，而维度关键属性（Dimension Key Attribute）处理时又需要这些数据，因而引发了这个“找不到属性键”的错误。这么讲可能有些抽象，你可以看我以前写的文章<a href="http://www.imkevinyang.com/2009/10/%E5%85%A8%E5%8D%8A%E8%A7%92%E7%A9%BA%E6%A0%BC%E5%AF%BC%E8%87%B4%E7%9A%84analysis-service%E5%A4%84%E7%90%86%E9%94%99%E8%AF%AF.html">全半角空格导致的Analysis Services处理错误</a>，里头有具体的案例。</p><p>解决这种问题就需要定位到出问题的数据。而生产环境中数据量一般会比较大，我们不大可能检查所有数据来发现可疑数据，这个时候，SSAS给出的错误提示就很有帮助了。</p><p>通常遇到处理异常，SSAS会有两种错误信息提示，一种是</p><blockquote><p>Errors in the OLAP storage engine: The attribute key cannot be found: Table: TableName, Column: ColumnName1, Value: Value1. Table: TableName, Column: ColumnName2, Value: Value2.</p></blockquote><p>这个错误提示会给出数据源中出错的数据，是在哪个表的哪个字段哪个值出的问题。</p><p>还有一个是</p><blockquote><p>Errors in the OLAP storage engine: The record was skipped because the attribute key was not found. Attribute: generated attribute X of Dimension: DimensionName from Database: DatabaseName, Cube: CubeName, Measure Group: MeasureGroupName, Partition: PartitionName, Record: <font color="#ff0000">RecordNumber</font>.</p></blockquote><p>这个错误信息给出的是Analysis数据库中出错的位置，是哪个AS数据库的哪个数据立方体的哪个维度的哪个维度属性出的问题，以及出错条目编号是多少。</p><p>如果出错数据比较好辨认的话，那么我们可以直接在源数据库中使用like操作符查询相关的条目即可。例如下面这个例子：</p><p><img style="display: inline" class="wlDisabledImage" title="image[36]" alt="image[36]" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image36.png" width="534" height="28" /></p><p>我们可以很快将出错数据锁定在一个较小的范围。但是如果错误信息像下面这样：</p><p><img style="margin: 0px; display: inline" class="wlDisabledImage" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image1.png" width="279" height="26" /></p><p>你可能就没那么好定位了，这个时候我们就需要第二个错误提示的帮助了。</p><p>为了在处理失败时显示第二个错误提示，我们需要修改维度的ErrorConfiguration属性。打开维度的属性面板，展开ErrorConfiguration中的KeyNotFound，BIDS中如果你新建一个维度，那么默认配置会像下面这样：</p><p><img style="margin: 0px; display: inline" class="wlDisabledImage" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image2.png" width="343" height="198" /></p><p>如果处理失败，是不会显示第二个错误提示的。我们需要将KeyNotFound设置为ReportAndContinue，或者直接把整个ErrorConfiguration从(custom)改成(default)。</p><p><img style="margin: 0px; display: inline" class="wlDisabledImage" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image3.png" width="349" height="202" /></p><p>这样我们就能得到第二个错误提示了，第二个错误提示中有一个“记录(Record)”的编号，那么这个编号代表什么含义呢？</p><p><img style="margin: 0px; display: inline" class="wlDisabledImage" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image4.png" width="475" height="49" /></p><p>我一开始直觉的认为是EmployeeKey的主键键值，后来发现不对，于是猜测是处理时执行的Sql查询返回的结果集的行编号，后来发现又错了。</p><p><img style="margin: 0px; display: inline" class="wlDisabledImage" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image5.png" width="588" height="186" /></p><p>在经过一番测试后发现，错误提示中的“记录”编号，确实是和Sql查询返回的条目的行编号有关，但却有一定的偏移，一般情况下这个偏移值为1，也就是说，假设SSAS告诉你“记录”7出错了，那么就是第6行的数据出错了。</p><p><img style="margin: 0px; display: inline" class="wlDisabledImage" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image6.png" width="262" height="158" /></p><p>但有时候这个偏移不一定是1，有可能是2。至于规律和原因我暂时没找到。所以你只需要把出问题的维度属性相关的Sql查询放到Sql Server中运行一遍，然后根据错误提示中的“记录”号找到那附近的几行数据，一般就可以成功定位到错误数据了。</p><p style="text-align: right">——<a title="SSAS处理时“找不到属性键”的解决办法" href="http://www.imkevinyang.com/2010/07/SSAS%e5%a4%84%e7%90%86%e6%97%b6%e2%80%9c%e6%89%be%e4%b8%8d%e5%88%b0%e5%b1%9e%e6%80%a7%e9%94%ae%e2%80%9d%e7%9a%84%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95.html"><em>Kevin Yang</em></a></p>标签：<a href="http://www.imkevinyang.com/tags/analysis-services" title="Analysis Services" rel="tag">Analysis Services</a>, <a href="http://www.imkevinyang.com/categories/techarticles/businessintelligence" title="BI/数据库" rel="tag">BI/数据库</a>, <a href="http://www.imkevinyang.com/tags/ssas" title="SSAS" rel="tag">SSAS</a>, <a href="http://www.imkevinyang.com/tags/%e5%a4%84%e7%90%86%e5%bc%82%e5%b8%b8" title="处理异常" rel="tag">处理异常</a>, <a href="http://www.imkevinyang.com/tags/%e5%b1%9e%e6%80%a7%e9%94%ae" title="属性键" rel="tag">属性键</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2010/05/64%e4%bd%8d%e7%b3%bb%e7%bb%9f%e4%b8%8biis7-isapi%e5%a4%84%e7%90%86%e5%99%a8%e5%8a%a0%e8%bd%bd%e5%a4%b1%e8%b4%a5.html" title="64位系统下IIS7 ISAPI处理器加载失败 (2010/05/05)">64位系统下IIS7 ISAPI处理器加载失败</a> (2010/05/05)</li><li><a href="http://www.imkevinyang.com/2009/04/analysis-services-2005-olap%e8%ae%be%e8%ae%a1%e6%9c%80%e4%bd%b3%e5%ae%9e%e8%b7%b5.html" title="Analysis Services 2005 OLAP设计最佳实践 (2009/04/02)">Analysis Services 2005 OLAP设计最佳实践</a> (2009/04/02)</li><li><a href="http://www.imkevinyang.com/2010/04/analysis-services%ef%bc%9a%e4%bd%a0%e5%ba%94%e8%af%a5%e4%bd%bf%e7%94%a8%e5%a4%9a%e5%af%b9%e5%a4%9a%e7%bb%b4%e5%ba%a6%e5%90%97%ef%bc%9f.html" title="Analysis Services：你应该使用多对多维度吗？ (2010/04/12)">Analysis Services：你应该使用多对多维度吗？</a> (2010/04/12)</li><li><a href="http://www.imkevinyang.com/2009/03/bids%e5%af%b9%e4%ba%8e%e7%bb%b4%e5%ba%a6%e5%b1%9e%e6%80%a7%e9%94%ae%e4%b8%ba%e5%8f%af%e5%8f%98%e9%95%bf%e5%88%97%e6%97%b6%e5%ad%98%e5%9c%a8%e7%9a%84bug.html" title="BIDS对于维度属性键为可变长列时存在的Bug (2009/03/22)">BIDS对于维度属性键为可变长列时存在的Bug</a> (2009/03/22)</li><li><a href="http://www.imkevinyang.com/2009/03/bids%e9%83%a8%e7%bd%b2%e6%97%b6%e5%87%ba%e7%8e%b0%e5%85%83%e6%95%b0%e6%8d%ae%e7%ae%a1%e7%90%86%e5%99%a8%e5%8f%91%e7%94%9f%e9%94%99%e8%af%af.html" title="BIDS部署时出现元数据管理器发生错误 (2009/03/13)">BIDS部署时出现元数据管理器发生错误</a> (2009/03/13)</li><li><a href="http://www.imkevinyang.com/2009/08/olap%e4%b8%ad%e7%9a%84averageofchildren%e8%81%9a%e5%90%88%e6%96%b9%e5%bc%8f.html" title="OLAP中的AverageOfChildren聚合方式 (2009/08/10)">OLAP中的AverageOfChildren聚合方式</a> (2009/08/10)</li><li><a href="http://www.imkevinyang.com/2009/10/%e5%85%a8%e5%8d%8a%e8%a7%92%e7%a9%ba%e6%a0%bc%e5%af%bc%e8%87%b4%e7%9a%84analysis-service%e5%a4%84%e7%90%86%e9%94%99%e8%af%af.html" title="全半角空格导致的Analysis Services处理错误 (2009/10/09)">全半角空格导致的Analysis Services处理错误</a> (2009/10/09)</li><li><a href="http://www.imkevinyang.com/2009/11/%e6%9d%83%e9%99%90%e5%af%bc%e8%87%b4%e7%9a%84analysis%e6%80%a7%e8%83%bd%e8%ae%a1%e6%95%b0%e5%99%a8%e5%9c%a8vistawin2008%e4%b8%8b%e5%a4%b1%e6%95%88%e7%9a%84%e9%97%ae%e9%a2%98.html" title="权限不足导致的Analysis性能计数器在Vista/win2008下失效的问题 (2009/11/12)">权限不足导致的Analysis性能计数器在Vista/win2008下失效的问题</a> (2009/11/12)</li><li><a href="http://www.imkevinyang.com/2009/09/%e8%a7%a3%e5%86%b3%e7%bb%b4%e5%ba%a6%e6%88%90%e5%91%98%e4%b8%ad%e7%9a%84%e9%9d%9e%e6%b3%95xml%e5%ad%97%e7%ac%a6%e5%af%bc%e8%87%b4%e7%9a%84%e6%9f%a5%e8%af%a2%e9%94%99%e8%af%af-2.html" title="解决维度成员中的非法Xml字符导致的查询错误 (2009/09/19)">解决维度成员中的非法Xml字符导致的查询错误</a> (2009/09/19)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/07/ssas%e5%a4%84%e7%90%86%e6%97%b6%e6%89%be%e4%b8%8d%e5%88%b0%e5%b1%9e%e6%80%a7%e9%94%ae%e7%9a%84%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95.html/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Visual Studio编辑器一次缩进/反缩进4个空格</title><link>http://www.imkevinyang.com/2010/07/visual-studio%e7%bc%96%e8%be%91%e5%99%a8%e4%b8%80%e6%ac%a1%e7%bc%a9%e8%bf%9b%e5%8f%8d%e7%bc%a9%e8%bf%9b4%e4%b8%aa%e7%a9%ba%e6%a0%bc.html</link> <comments>http://www.imkevinyang.com/2010/07/visual-studio%e7%bc%96%e8%be%91%e5%99%a8%e4%b8%80%e6%ac%a1%e7%bc%a9%e8%bf%9b%e5%8f%8d%e7%bc%a9%e8%bf%9b4%e4%b8%aa%e7%a9%ba%e6%a0%bc.html#comments</comments> <pubDate>Sun, 04 Jul 2010 02:00:00 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[工具技巧]]></category> <category><![CDATA[IDE]]></category> <category><![CDATA[Tab]]></category> <category><![CDATA[Visual Studio]]></category> <category><![CDATA[空格]]></category> <category><![CDATA[缩进]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/07/visual-studio%e7%bc%96%e8%be%91%e5%99%a8%e4%b8%80%e6%ac%a1%e7%bc%a9%e8%bf%9b%e5%8f%8d%e7%bc%a9%e8%bf%9b4%e4%b8%aa%e7%a9%ba%e6%a0%bc.html</guid> <description><![CDATA[<p>在代码中使用Tab作为缩进并不是一个好的习惯，因为Tab并不是一个打印字符，而是一个控制字符，也就是说它在不同文本编辑器中显示的宽度不一定是相同的，因而显示效果容易产生不一致。而空格则具有很好的适应性，因为它就是一个普通字符，因此和其他字符一样受到相同的影响，显示效果会趋于一致。</p><p>那些使用Tab作为缩进的人通常是因为用Tab做缩进的话，可以一次删除一个缩进单位，非常方便。而如果按照一个Tab对应4个空格来转换的话，要删除一个缩进单位则需要&#8230;</p>]]></description> <content:encoded><![CDATA[</p><p>在代码中使用Tab作为缩进并不是一个好的习惯，因为Tab并不是一个打印字符，而是一个控制字符，也就是说它在不同文本编辑器中显示的宽度不一定是相同的，因而显示效果容易产生不一致。而空格则具有很好的适应性，因为它就是一个普通字符，因此和其他字符一样受到相同的影响，显示效果会趋于一致。</p><p>那些使用Tab作为缩进的人通常是因为用Tab做缩进的话，可以一次删除一个缩进单位，非常方便。而如果按照一个Tab对应4个空格来转换的话，要删除一个缩进单位则需要删除4个空格，相对不便。</p><p>实际上，现在很多文本编辑器包括Visual Studio对Tab和空格的转换都有很好的支持，如果你设置了坚持使用空格，那么通过<strong>快捷键Tab和Shift+Tab可以实现一次插入或者删除4个空格</strong>，和使用Tab做缩进的效果是一样的。</p><p>这下你使用空格做缩进就不会感到不便了。</p><p style="text-align: right">——<a title="Visual Studio编辑器一次缩进/反缩进4个空格" href="http://www.imkevinyang.com/2010/07/Visual%20Studio%e7%bc%96%e8%be%91%e5%99%a8%e4%b8%80%e6%ac%a1%e7%bc%a9%e8%bf%9b/%e5%8f%8d%e7%bc%a9%e8%bf%9b4%e4%b8%aa%e7%a9%ba%e6%a0%bc.html"><em>Kevin Yang</em></a></p>标签：<a href="http://www.imkevinyang.com/tags/ide" title="IDE" rel="tag">IDE</a>, <a href="http://www.imkevinyang.com/tags/tab" title="Tab" rel="tag">Tab</a>, <a href="http://www.imkevinyang.com/tags/visual-studio" title="Visual Studio" rel="tag">Visual Studio</a>, <a href="http://www.imkevinyang.com/categories/techarticles/toolskills-techarticles" title="工具技巧" rel="tag">工具技巧</a>, <a href="http://www.imkevinyang.com/tags/%e7%a9%ba%e6%a0%bc" title="空格" rel="tag">空格</a>, <a href="http://www.imkevinyang.com/tags/%e7%bc%a9%e8%bf%9b" title="缩进" rel="tag">缩进</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2009/09/visual-assist%e4%bb%a3%e7%a0%81%e6%8f%90%e7%a4%ba%e4%bd%bf%e7%94%a8%e7%9a%84%e5%ad%97%e7%ac%a6%e4%b8%b2%e6%a8%a1%e7%b3%8a%e5%8c%b9%e9%85%8d%e7%ae%97%e6%b3%95.html" title="Visual Assist代码提示使用的字符串模糊匹配算法 (2009/09/28)">Visual Assist代码提示使用的字符串模糊匹配算法</a> (2009/09/28)</li><li><a href="http://www.imkevinyang.com/2009/09/visual-studio%e5%b0%8f%e8%b4%b4%e5%a3%ab%e5%8a%a0%e4%ba%86try-catch%e4%b9%9f%e8%83%bd%e8%87%aa%e5%8a%a8%e5%ae%9a%e4%bd%8d%e5%88%b0%e5%bc%82%e5%b8%b8%e4%bb%a3%e7%a0%81.html" title="Visual Studio小贴士&mdash;&mdash;加了Try-Catch也能自动定位到异常代码 (2009/09/18)">Visual Studio小贴士&mdash;&mdash;加了Try-Catch也能自动定位到异常代码</a> (2009/09/18)</li><li><a href="http://www.imkevinyang.com/2009/05/visual-studio%e7%bb%9f%e8%ae%a1%e6%9c%89%e6%95%88%e4%bb%a3%e7%a0%81%e8%a1%8c%e6%95%b0.html" title="Visual Studio统计有效代码行数 (2009/05/22)">Visual Studio统计有效代码行数</a> (2009/05/22)</li><li><a href="http://www.imkevinyang.com/2010/05/vs2010%e7%9a%84ui%e8%ae%be%e8%ae%a1%e5%a4%b1%e8%af%af.html" title="VS2010的UI设计失误 (2010/05/31)">VS2010的UI设计失误</a> (2010/05/31)</li><li><a href="http://www.imkevinyang.com/2009/10/%e5%85%a8%e5%8d%8a%e8%a7%92%e7%a9%ba%e6%a0%bc%e5%af%bc%e8%87%b4%e7%9a%84analysis-service%e5%a4%84%e7%90%86%e9%94%99%e8%af%af.html" title="全半角空格导致的Analysis Services处理错误 (2009/10/09)">全半角空格导致的Analysis Services处理错误</a> (2009/10/09)</li><li><a href="http://www.imkevinyang.com/2009/09/%e8%a7%a3%e5%86%b3silverlight%e6%97%a0%e6%b3%95%e8%b0%83%e8%af%95%e7%9a%84%e9%97%ae%e9%a2%98.html" title="解决Silverlight无法调试的问题 (2009/09/01)">解决Silverlight无法调试的问题</a> (2009/09/01)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/07/visual-studio%e7%bc%96%e8%be%91%e5%99%a8%e4%b8%80%e6%ac%a1%e7%bc%a9%e8%bf%9b%e5%8f%8d%e7%bc%a9%e8%bf%9b4%e4%b8%aa%e7%a9%ba%e6%a0%bc.html/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Win7访问远程桌面最大化同时让任务栏可见</title><link>http://www.imkevinyang.com/2010/07/win7%e8%ae%bf%e9%97%ae%e8%bf%9c%e7%a8%8b%e6%a1%8c%e9%9d%a2%e6%9c%80%e5%a4%a7%e5%8c%96%e5%90%8c%e6%97%b6%e8%ae%a9%e4%bb%bb%e5%8a%a1%e6%a0%8f%e5%8f%af%e8%a7%81.html</link> <comments>http://www.imkevinyang.com/2010/07/win7%e8%ae%bf%e9%97%ae%e8%bf%9c%e7%a8%8b%e6%a1%8c%e9%9d%a2%e6%9c%80%e5%a4%a7%e5%8c%96%e5%90%8c%e6%97%b6%e8%ae%a9%e4%bb%bb%e5%8a%a1%e6%a0%8f%e5%8f%af%e8%a7%81.html#comments</comments> <pubDate>Sat, 03 Jul 2010 02:00:00 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[工具技巧]]></category> <category><![CDATA[Win7]]></category> <category><![CDATA[任务栏]]></category> <category><![CDATA[远程桌面]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/07/win7%e8%ae%bf%e9%97%ae%e8%bf%9c%e7%a8%8b%e6%a1%8c%e9%9d%a2%e6%9c%80%e5%a4%a7%e5%8c%96%e5%90%8c%e6%97%b6%e8%ae%a9%e4%bb%bb%e5%8a%a1%e6%a0%8f%e5%8f%af%e8%a7%81.html</guid> <description><![CDATA[<p>使用远程桌面时，默认情况下当连接成功后会自动全屏，或者当你点击最大化按钮时，也会进入全屏模式，这个时候就看不到任务栏了。这多少有点不便，这样一来任务栏上的通知就看不到了，例如邮件提醒，IM提醒等，除非你开着声音。</p><p>在Win7下我们可以先将远程桌面程序窗口恢复普通大小，然后按下快捷键“Win+方向键上”，这样就会就会变成以下这样了，保持任务栏可见的同时让远程桌面最大化。 <br /><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image.png" width="559" height="78" /></p><p style="text-align: right">——<a title="Win7访问远程桌面最大化同时让任务栏可见" href="http://www.imkevinyang.com/2010/07/Win7%e8%ae%bf%e9%97%ae%e8%bf%9c%e7%a8%8b%e6%a1%8c%e9%9d%a2%e6%9c%80%e5%a4%a7%e5%8c%96%e5%90%8c%e6%97%b6%e8%ae%a9%e4%bb%bb%e5%8a%a1%e6%a0%8f%e5%8f%af%e8%a7%81.html"><em>Kevin Yang</em></a></p>标签：<a href="http://www.imkevinyang.com/tags/win7" title="Win7" rel="tag">Win7</a>, <a href="http://www.imkevinyang.com/tags/%e4%bb%bb%e5%8a%a1%e6%a0%8f" title="任务栏" rel="tag">任务栏</a>, <a href="http://www.imkevinyang.com/categories/techarticles/toolskills-techarticles" title="工具技巧" rel="tag">工具技巧</a>, <a href="http://www.imkevinyang.com/tags/%e8%bf%9c%e7%a8%8b%e6%a1%8c%e9%9d%a2" title="远程桌面" rel="tag">远程桌面</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对&#8230;</h4>]]></description> <content:encoded><![CDATA[<p>使用远程桌面时，默认情况下当连接成功后会自动全屏，或者当你点击最大化按钮时，也会进入全屏模式，这个时候就看不到任务栏了。这多少有点不便，这样一来任务栏上的通知就看不到了，例如邮件提醒，IM提醒等，除非你开着声音。</p><p>在Win7下我们可以先将远程桌面程序窗口恢复普通大小，然后按下快捷键“Win+方向键上”，这样就会就会变成以下这样了，保持任务栏可见的同时让远程桌面最大化。 <br /><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/07/image.png" width="559" height="78" /></p><p style="text-align: right">——<a title="Win7访问远程桌面最大化同时让任务栏可见" href="http://www.imkevinyang.com/2010/07/Win7%e8%ae%bf%e9%97%ae%e8%bf%9c%e7%a8%8b%e6%a1%8c%e9%9d%a2%e6%9c%80%e5%a4%a7%e5%8c%96%e5%90%8c%e6%97%b6%e8%ae%a9%e4%bb%bb%e5%8a%a1%e6%a0%8f%e5%8f%af%e8%a7%81.html"><em>Kevin Yang</em></a></p>标签：<a href="http://www.imkevinyang.com/tags/win7" title="Win7" rel="tag">Win7</a>, <a href="http://www.imkevinyang.com/tags/%e4%bb%bb%e5%8a%a1%e6%a0%8f" title="任务栏" rel="tag">任务栏</a>, <a href="http://www.imkevinyang.com/categories/techarticles/toolskills-techarticles" title="工具技巧" rel="tag">工具技巧</a>, <a href="http://www.imkevinyang.com/tags/%e8%bf%9c%e7%a8%8b%e6%a1%8c%e9%9d%a2" title="远程桌面" rel="tag">远程桌面</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2009/12/win7vista%e9%ab%98%e7%ba%a7%e7%94%a8%e6%88%b7%e5%bf%85%e5%a4%87%e7%9a%84%e5%8f%b3%e9%94%ae%e8%8f%9c%e5%8d%95take-back-my-ownership.html" title="Win7/Vista高级用户必备的右键菜单Take back my Ownership! (2009/12/06)">Win7/Vista高级用户必备的右键菜单Take back my Ownership!</a> (2009/12/06)</li><li><a href="http://www.imkevinyang.com/2009/11/%e8%a7%a3%e5%86%b3win7%e4%b8%8bie8%e5%bc%80%e5%8f%91%e4%ba%ba%e5%91%98%e5%b7%a5%e5%85%b7%e7%aa%97%e5%8f%a3%e6%b6%88%e5%a4%b1%e7%9a%84%e9%97%ae%e9%a2%98.html" title="解决Win7下IE8开发人员工具打不开的问题 (2009/11/13)">解决Win7下IE8开发人员工具打不开的问题</a> (2009/11/13)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/07/win7%e8%ae%bf%e9%97%ae%e8%bf%9c%e7%a8%8b%e6%a1%8c%e9%9d%a2%e6%9c%80%e5%a4%a7%e5%8c%96%e5%90%8c%e6%97%b6%e8%ae%a9%e4%bb%bb%e5%8a%a1%e6%a0%8f%e5%8f%af%e8%a7%81.html/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Sql Tips——Update语句也使用表别名(Table Alias)</title><link>http://www.imkevinyang.com/2010/07/sql-tipsupdate%e8%af%ad%e5%8f%a5%e4%b9%9f%e4%bd%bf%e7%94%a8%e8%a1%a8%e5%88%ab%e5%90%8dtable-alias.html</link> <comments>http://www.imkevinyang.com/2010/07/sql-tipsupdate%e8%af%ad%e5%8f%a5%e4%b9%9f%e4%bd%bf%e7%94%a8%e8%a1%a8%e5%88%ab%e5%90%8dtable-alias.html#comments</comments> <pubDate>Thu, 01 Jul 2010 16:11:21 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[BI/数据库]]></category> <category><![CDATA[其他随笔]]></category> <category><![CDATA[SQL]]></category> <category><![CDATA[Tips]]></category> <category><![CDATA[Update]]></category> <category><![CDATA[数据库]]></category> <category><![CDATA[表别名]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/07/sql-tipsupdate%e8%af%ad%e5%8f%a5%e4%b9%9f%e4%bd%bf%e7%94%a8%e8%a1%a8%e5%88%ab%e5%90%8dtable-alias.html</guid> <description><![CDATA[<p>在编写Sql脚本时通过表别名可以大大缩减Sql代码，同时表别名也是解决同表多次引用的手段之一。在select中使用表别名大家应该都很熟悉了：</p><pre class="brush: sql">select * from TableA as A inner join TableB as B on A.Key1 = B.Key1</pre><p>但是在Update中使用表别名可能就没那么多人知道了。</p><pre class="brush: sql">update T
set T.Key1 = 'xxxx'
from TableA T</pre><p>这些天在写Sql Update脚本的时候需要引用两次同个表对象，如果直接像&#8230;</p>]]></description> <content:encoded><![CDATA[<p>在编写Sql脚本时通过表别名可以大大缩减Sql代码，同时表别名也是解决同表多次引用的手段之一。在select中使用表别名大家应该都很熟悉了：</p><pre class="brush: sql">select * from TableA as A inner join TableB as B on A.Key1 = B.Key1</pre><p>但是在Update中使用表别名可能就没那么多人知道了。</p><pre class="brush: sql">update T
set T.Key1 = 'xxxx'
from TableA T</pre><p>这些天在写Sql Update脚本的时候需要引用两次同个表对象，如果直接像下面这样引用两次TableA则会抛出“The multi-part identifier ‘TableA.Index’ could not be bound”的错误。这是因为Sql引擎无法知道你在where子句中的TableA到底指的是要Update的表还是from后面的表。</p><pre class="brush: sql">update TableA
set TableA.NextKey = T.Key
from TableA T
where T.Index = TableA.Index + 1</pre><p>如果不对Update后面的TableA使用别名的话，我们只能通过以下方法来实现。</p><pre class="brush: sql">update TableA
set TableA.NextKey = T.Key
from
(
  select * from TableA
)T
where T.Index = TableA.Index + 1</pre><p>使用别名可以得到更简洁的写法:</p><pre class="brush: sql">update T1
set T1.NextKey = T2.Key
from TableA T1, TableA T2
whereT2.Index = T1.Index + 1</pre><p style="text-align: right">——<a title="Sql Tips——Update语句也使用表别名(Table Alias)" href="http://www.imkevinyang.com/2010/07/Sql%20Tips%e2%80%94%e2%80%94Update%e8%af%ad%e5%8f%a5%e4%b9%9f%e4%bd%bf%e7%94%a8%e8%a1%a8%e5%88%ab%e5%90%8d(Table%20Alias).html"><em>Kevin Yang</em></a></p>标签：<a href="http://www.imkevinyang.com/categories/techarticles/businessintelligence" title="BI/数据库" rel="tag">BI/数据库</a>, <a href="http://www.imkevinyang.com/tags/sql" title="SQL" rel="tag">SQL</a>, <a href="http://www.imkevinyang.com/tags/tips" title="Tips" rel="tag">Tips</a>, <a href="http://www.imkevinyang.com/tags/update" title="Update" rel="tag">Update</a>, <a href="http://www.imkevinyang.com/categories/techarticles/othertecharticles" title="其他随笔" rel="tag">其他随笔</a>, <a href="http://www.imkevinyang.com/tags/%e6%95%b0%e6%8d%ae%e5%ba%93" title="数据库" rel="tag">数据库</a>, <a href="http://www.imkevinyang.com/tags/%e8%a1%a8%e5%88%ab%e5%90%8d" title="表别名" rel="tag">表别名</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2009/09/blend%e5%b0%8f%e8%b4%b4%e5%a3%ab%e6%94%b9%e5%8f%98%e9%bc%a0%e6%a0%87%e6%bb%9a%e8%bd%ae%e7%9a%84%e9%bb%98%e8%ae%a4%e8%a1%8c%e4%b8%ba-2.html" title="Blend小贴士&mdash;&mdash;改变鼠标滚轮的默认行为 (2009/09/18)">Blend小贴士&mdash;&mdash;改变鼠标滚轮的默认行为</a> (2009/09/18)</li><li><a href="http://www.imkevinyang.com/2009/05/sql-server%e4%b8%ad%e4%bf%ae%e6%94%b9%e8%87%aa%e5%a2%9e%e9%95%bf%e5%88%97%e7%bb%8f%e5%b8%b8%e9%9c%80%e8%a6%81%e7%94%a8%e5%88%b0%e7%9a%84%e9%85%8d%e7%bd%ae.html" title="Sql Server中修改自增长列经常需要用到的配置 (2009/05/24)">Sql Server中修改自增长列经常需要用到的配置</a> (2009/05/24)</li><li><a href="http://www.imkevinyang.com/2010/05/ssis%e8%b0%83%e7%94%a8%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e5%a4%b1%e8%b4%a5.html" title="SSIS调用存储过程失败 (2010/05/23)">SSIS调用存储过程失败</a> (2010/05/23)</li><li><a href="http://www.imkevinyang.com/2009/09/visual-studio%e5%b0%8f%e8%b4%b4%e5%a3%ab%e5%8a%a0%e4%ba%86try-catch%e4%b9%9f%e8%83%bd%e8%87%aa%e5%8a%a8%e5%ae%9a%e4%bd%8d%e5%88%b0%e5%bc%82%e5%b8%b8%e4%bb%a3%e7%a0%81.html" title="Visual Studio小贴士&mdash;&mdash;加了Try-Catch也能自动定位到异常代码 (2009/09/18)">Visual Studio小贴士&mdash;&mdash;加了Try-Catch也能自动定位到异常代码</a> (2009/09/18)</li><li><a href="http://www.imkevinyang.com/2009/09/%e9%87%8a%e6%94%besql-server%e5%8d%a0%e7%94%a8%e7%9a%84%e5%86%85%e5%ad%98.html" title="释放SQL Server占用的内存 (2009/09/01)">释放SQL Server占用的内存</a> (2009/09/01)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/07/sql-tipsupdate%e8%af%ad%e5%8f%a5%e4%b9%9f%e4%bd%bf%e7%94%a8%e8%a1%a8%e5%88%ab%e5%90%8dtable-alias.html/feed</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>关于字符编码，你所需要知道的</title><link>http://www.imkevinyang.com/2010/06/%e5%85%b3%e4%ba%8e%e5%ad%97%e7%ac%a6%e7%bc%96%e7%a0%81%ef%bc%8c%e4%bd%a0%e6%89%80%e9%9c%80%e8%a6%81%e7%9f%a5%e9%81%93%e7%9a%84.html</link> <comments>http://www.imkevinyang.com/2010/06/%e5%85%b3%e4%ba%8e%e5%ad%97%e7%ac%a6%e7%bc%96%e7%a0%81%ef%bc%8c%e4%bd%a0%e6%89%80%e9%9c%80%e8%a6%81%e7%9f%a5%e9%81%93%e7%9a%84.html#comments</comments> <pubDate>Fri, 18 Jun 2010 13:41:05 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[其他随笔]]></category> <category><![CDATA[ASCII]]></category> <category><![CDATA[GB18030]]></category> <category><![CDATA[GB2312]]></category> <category><![CDATA[GBK]]></category> <category><![CDATA[OEM]]></category> <category><![CDATA[Unicode]]></category> <category><![CDATA[UTF-16]]></category> <category><![CDATA[UTF-8]]></category> <category><![CDATA[字符]]></category> <category><![CDATA[字符编解码]]></category> <category><![CDATA[编码]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/06/%e5%85%b3%e4%ba%8e%e5%ad%97%e7%ac%a6%e7%bc%96%e7%a0%81%ef%bc%8c%e4%bd%a0%e6%89%80%e9%9c%80%e8%a6%81%e7%9f%a5%e9%81%93%e7%9a%84.html</guid> <description><![CDATA[<p>字符编码的问题看似很小，经常被技术人员忽视，但是很容易导致一些莫名其妙的问题。这里总结了一下字符编码的一些普及性的知识，希望对大家有所帮助。</p><h2>还是得从ASCII码说起</h2><p>说到字符编码，不得不说ASCII码的简史。计算机一开始发明的时候是用来解决数字计算的问题，后来人们发现，计算机还可以做更多的事，例如文本处理。但由于计算机只识“数”，因此人们必须告诉计算机哪个数字来代表哪个特定字符，例如65代表字母‘A’，66代表字母‘B’，以此类推。但是<strong>计&#8230;</strong></p>]]></description> <content:encoded><![CDATA[<p>字符编码的问题看似很小，经常被技术人员忽视，但是很容易导致一些莫名其妙的问题。这里总结了一下字符编码的一些普及性的知识，希望对大家有所帮助。</p><h2>还是得从ASCII码说起</h2></p><p>说到字符编码，不得不说ASCII码的简史。计算机一开始发明的时候是用来解决数字计算的问题，后来人们发现，计算机还可以做更多的事，例如文本处理。但由于计算机只识“数”，因此人们必须告诉计算机哪个数字来代表哪个特定字符，例如65代表字母‘A’，66代表字母‘B’，以此类推。但是<strong>计算机之间字符-数字的对应关系必须得一致，否则就会造成同一段数字在不同计算机上显示出来的字符不一样</strong>。因此美国国家标准协会ANSI制定了一个标准，规定了常用字符的集合以及每个字符对应的编号，这就是ASCII字符集（Character Set），也称ASCII码。</p><p>当时的计算机普遍使用8比特字节作为最小的存储和处理单元，加之当时用到的字符也很少，26个大小写英文字母还有数字再加上其他常用符号，也不到100个，因此使用7个比特位就可以高效的存储和处理ASCII码，剩下最高位1比特被用作一些通讯系统的奇偶校验。</p><blockquote><p>注意，字节代表系统能够处理的最小单位，不一定是8比特。只是现代计算机的事实标准就是用8比特来代表一个字节。在很多技术规格文献中，为了避免产生歧义，更倾向于使用8位组（Octet）而不是字节（Byte）这个术语来强调8个比特的二进制流。下文中为了便于理解，我会延用大家熟悉的“字节”这个概念。</p></blockquote><p><img style="display: inline; margin-left: 0px; margin-right: 0px" alt="ASCII table" src="http://www.joelonsoftware.com/pictures/unicode/ascii.png" width="274" height="146" /></p><p>ASCII字符集由95个可打印字符（0x20-0x7E）和33个控制字符（0x00-0x19，0x7F）组成。可打印字符用于显示在输出设备上，例如荧屏或者打印纸上，控制字符用于向计算机发出一些特殊指令，例如0x07会让计算机发出哔的一声，0x00通常用于指示字符串的结束，0x0D和0x0A用于指示打印机的打印针头退到行首（回车）并移到下一行（换行）。</p><p>那时候的字符编解码系统非常简单，就是简单的查表过程。例如将字符序列编码为二进制流写入存储设备，只需要在ASCII字符集中依次找到字符对应的字节，然后直接将该字节写入存储设备即可。解码二进制流的过程也是类似。</p><h2>OEM字符集的衍生</h2><p>当计算机开始发展起来的时候，人们逐渐发现，ASCII字符集里那可怜的128个字符已经不能再满足他们的需求了。人们就在想，一个字节能够表示的数字（编号）有256个，而ASCII字符只用到了0x00~0x7F，也就是占用了前128个，后面128个数字不用白不用，因此很多人打起了后面这128个数字的主意。可是问题在于，很多人同时有这样的想法，但是大家对于0x80-0xFF这后面的128个数字分别对应什么样的字符，却有各自的想法。这就导致了当时销往世界各地的机器上出现了大量各式各样的OEM字符集。</p><p>下面这张表是IBM-PC机推出的其中一个OEM字符集，字符集的前128个字符和ASCII字符集的基本一致（为什么说基本一致呢，是因为前32个控制字符在某些情况下会被IBM-PC机当作可打印字符解释），后面128个字符空间加入了一些欧洲国家用到的重音字符，以及一些用于画线条画的字符。</p><p><img style="display: inline" title="IBM-PC OEM字符集" alt="IBM-PC OEM字符集" src="http://www.joelonsoftware.com/pictures/unicode/oem.png" width="271" height="209" /></p><p>事实上，大部分OEM字符集是兼容ASCII字符集的，也就是说，大家对于0x00~0x7F这个范围的解释基本是相同的，而对于后半部分0x80~0xFF的解释却不一定相同。甚至有时候同样的字符在不同OEM字符集中对应的字节也是不同的。</p><p>不同的OEM字符集导致人们无法跨机器交流各种文档。例如职员甲发了一封简历résumés给职员乙，结果职员乙看到的却是r<img alt="?" src="http://www.joelonsoftware.com/pictures/unicode/gimel.png" width="5" height="9" />sum<img alt="?" src="http://www.joelonsoftware.com/pictures/unicode/gimel.png" width="5" height="9" />s，因为é字符在职员甲机器上的OEM字符集中对应的字节是0x82，而在职员乙的机器上，由于使用的OEM字符集不同，对0x82字节解码后得到的字符却是<img alt="?" src="http://www.joelonsoftware.com/pictures/unicode/gimel.png" width="5" height="9" />。</p><h2>多字节字符集（MBCS）和中文字符集</h2><p>上面我们提到的字符集都是基于单字节编码，也就是说，一个字节翻译成一个字符。这对于拉丁语系国家来说可能没有什么问题，因为他们通过扩展第8个比特，就可以得到256个字符了，足够用了。但是对于亚洲国家来说，256个字符是远远不够用的。因此这些国家的人为了用上电脑，又要保持和ASCII字符集的兼容，就发明了多字节编码方式，相应的字符集就称为多字节字符集。例如中国使用的就是双字节字符集编码（DBCS，Double Byte Character Set）。</p><p>对于单字节字符集来说，代码页中只需要有一张码表即可，上面记录着256个数字代表的字符。程序只需要做简单的查表操作就可以完成编解码的过程。</p><blockquote><p>代码页是字符集编码的具体实现，你可以把他理解为一张“字符-字节”映射表，通过查表实现“字符-字节”的翻译。下面会有更详细的描述。</p></blockquote><p>而对于多字节字符集，代码页中通常会有很多码表。那么程序怎么知道该使用哪张码表去解码二进制流呢？答案是，<strong>根据第一个字节来选择不同的码表进行解析</strong>。</p><p>例如目前最常用的中文字符集GB2312，涵盖了所有简体字符以及一部分其他字符；GBK（K代表扩展的意思）则在GB2312的基础上加入了对繁体字符等其他非简体字符（GB18030字符集不是双字节字符集，我们在讲Unicode的时候会提到）。这两个字符集的字符都是使用1-2个字节来表示。Windows系统采用936代码页来实现对GBK字符集的编解码。在解析字节流的时候，如果遇到字节的最高位是0的话，那么就使用936代码页中的第1张码表进行解码，这就和单字节字符集的编解码方式一致了。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/06/image15.png" width="416" height="305" /></p><p>当字节的高位是1的时候，确切的说，当第一个字节位于0x<code>81</code>–0x<code>FE之间时，根据第一个字节不同找到代码页中的相应的码表，例如当第一个字节是0x81，那么对应936中的下面这张码表：</code></p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/06/image9.png" width="442" height="374" /></p><p>（关于936代码页中完整的码表信息，参见MSDN：<a title="http://msdn.microsoft.com/en-us/library/cc194913%28v=MSDN.10%29.aspx" href="http://msdn.microsoft.com/en-us/library/cc194913%28v=MSDN.10%29.aspx">http://msdn.microsoft.com/en-us/library/cc194913%28v=MSDN.10%29.aspx</a>.）</p><p>按照936代码页的码表，当程序遇到连续字节流0x81 0x40的时候，就会解码为“丂”字符。</p><h2>ANSI标准、国家标准、ISO标准</h2><p>不同ASCII衍生字符集的出现，让文档交流变得非常困难，因此各种组织都陆续进行了标准化流程。例如美国ANSI组织制定了ANSI标准字符编码（注意，<strong>我们现在通常说到ANSI编码，通常指的是平台的默认编码，例如英文操作系统中是ISO-8859-1，中文系统是GBK</strong>），ISO组织制定的各种ISO标准字符编码，还有各国也会制定一些国家标准字符集，例如中国的GBK，GB2312和GB18030。</p><p>操作系统在发布的时候，通常会往机器里预装这些标准的字符集还有平台专用的字符集，这样只要你的文档是使用标准字符集编写的，通用性就比较高了。例如你用GB2312字符集编写的文档，在中国大陆内的任何机器上都能正确显示。同时，我们也可以在一台机器上阅读多个国家不同语言的文档了，前提是本机必须安装该文档使用的字符集。</p><h2>Unicode的出现</h2><p>虽然通过使用不同字符集，我们可以在一台机器上查阅不同语言的文档，但是我们仍然无法解决一个问题：<strong>在一份文档中显示所有字符</strong>。为了解决这个问题，我们需要一个全人类达成共识的巨大的字符集，这就是Unicode字符集。</p><h3>Unicode字符集概述</h3><p>Unicode字符集涵盖了目前人类使用的所有字符，并为每个字符进行统一编号，分配唯一的字符码（Code Point）。Unicode字符集将所有字符按照使用上的频繁度划分为17个层面（Plane），每个层面上有2<sup>16</sup>=65536个字符码空间。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/06/image29.png" width="556" height="224" /></p><p>其中第0个层面BMP，基本涵盖了当今世界用到的所有字符。其他的层面要么是用来表示一些远古时期的文字，要么是留作扩展。我们平常用到的Unicode字符，一般都是位于BMP层面上的。目前Unicode字符集中尚有大量字符空间未使用。</p><h3>编码系统的变化</h3><p>在Unicode出现之前，所有的字符集都是和具体编码方案绑定在一起的，都是直接将字符和最终字节流绑定死了，例如ASCII编码系统规定使用7比特来编码ASCII字符集；GB2312以及GBK字符集，限定了使用最多2个字节来编码所有字符，并且规定了字节序。这样的编码系统通常用简单的查表，也就是通过代码页就可以直接将字符映射为存储设备上的字节流了。例如下面这个例子：</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/06/image17.png" width="373" height="275" /></p><p>这种方式的缺点在于，字符和字节流之间耦合得太紧密了，从而限定了字符集的扩展能力。假设以后火星人入住地球了，要往现有字符集中加入火星文就变得很难甚至不可能了，而且很容易破坏现有的编码规则。</p><p>因此Unicode在设计上考虑到了这一点，将字符集和字符编码方案分离开。</p><p><img style="display: inline" title="字符编码系统" alt="字符编码系统" src="http://www.imkevinyang.com/wp-content/uploads/2010/06/image10.png" width="329" height="265" /></p><p>也就是说，<strong>虽然每个字符在Unicode字符集中都能找到唯一确定的编号（字符码，又称Unicode码），但是决定最终字节流的却是具体的字符编码</strong>。例如同样是对Unicode字符“A”进行编码，UTF-8字符编码得到的字节流是0x41，而UTF-16（大端模式）得到的是0x00 0x41。</p><h3>常见的Unicode编码</h3><p><strong>UCS-2/UTF-16</strong></p><p>如果要我们来实现Unicode字符集中BMP字符的编码方案，我们会怎么实现？由于BMP层面上有2<sup>16</sup>=65536个字符码，因此我们只需要两个字节就可以完全表示这所有的字符了。</p><p>举个例子，“中”的Unicode字符码是0x4E2D(01001110 00101101)，那么我们可以编码为01001110 00101101（大端）或者00101101 01001110 （小端）。</p><p>UCS-2和UTF-16对于BMP层面的字符均是使用2个字节来表示，并且编码得到的结果完全一致。不同之处在于，<strong>UCS-2最初设计的时候只考虑到BMP字符，因此使用固定2个字节长度，也就是说，他无法表示Unicode其他层面上的字符，而UTF-16为了解除这个限制，支持Unicode全字符集的编解码，采用了变长编码，最少使用2个字节，如果要编码BMP以外的字符，则需要4个字节结对</strong>，这里就不讨论那么远，有兴趣可以参考维基百科：<a title="UTF-16/UCS-2" href="http://en.wikipedia.org/wiki/UTF-16/UCS-2" target="_blank">UTF-16/UCS-2</a>。</p><p>Windows从NT时代开始就采用了UTF-16编码，很多流行的编程平台，例如.Net，Java，Qt还有Mac下的Cocoa等都是使用UTF-16作为基础的字符编码。例如代码中的字符串，在内存中相应的字节流就是用UTF-16编码过的。</p><p><strong>UTF-8</strong></p><p>UTF-8应该是目前应用最广泛的一种Unicode编码方案。由于UCS-2/UTF-16对于ASCII字符使用两个字节进行编码，存储和处理效率相对低下，并且由于ASCII字符经过UTF-16编码后得到的两个字节，高字节始终是0x00，很多C语言的函数都将此字节视为字符串末尾从而导致无法正确解析文本。因此一开始推出的时候遭到很多西方国家的抵触，大大影响了Unicode的推行。后来聪明的人们发明了UTF-8编码，解决了这个问题。</p><p>UTF-8编码方案采用1-4个字节来编码字符，方法其实也非常简单。</p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/06/image11.png" width="492" height="345" /></p><p>（上图中的x代表Unicode码的低8位，y代表高8位）</p><p><strong>对于ASCII字符的编码使用单字节，和ASCII编码一摸一样，这样所有原先使用ASCII编解码的文档就可以直接转到UTF-8编码了。对于其他字符，则使用2-4个字节来表示，其中，首字节前置1的数目代表正确解析所需要的字节数，剩余字节的高2位始终是10。例如首字节是1110yyyy，前置有3个1，说明正确解析总共需要3个字节，需要和后面2个以10开头的字节结合才能正确解析得到字符</strong>。</p><p>关于UTF-8的更多信息，参考维基百科：<a title="UTF-8" href="http://en.wikipedia.org/wiki/UTF-8" target="_blank">UTF-8</a>。</p><p><strong>GB18030</strong></p><p>任何能够将Unicode字符映射为字节流的编码都属于Unicode编码。中国的GB18030编码，覆盖了Unicode所有的字符，因此也算是一种Unicode编码。只不过他的编码方式并不像UTF-8或者UTF-16一样，将Unicode字符的编号通过一定的规则进行转换，而只能通过查表的手段进行编码。</p><p>关于GB18030的更多信息，参考：<a title="GB18030" href="http://en.wikipedia.org/wiki/GB18030" target="_blank">GB18030</a>。</p><h3>Unicode相关的常见问题</h3><p><strong>Unicode是两个字节吗？</strong></p><p>Unicode只是定义了一个庞大的、全球通用的字符集，并为每个字符规定了唯一确定的编号，具体存储为什么样的字节流，取决于字符编码方案。推荐的Unicode编码是UTF-16和UTF-8。</p><p><strong>带签名的UTF-8指的是什么意思？</strong></p><p>带签名指的是字节流以BOM标记开始。很多软件会“智能”的探测当前字节流使用的字符编码，这种探测过程出于效率考虑，通常会提取字节流前面若干个字节，看看是否符合某些常见字符编码的编码规则。由于UTF-8和ASCII编码对于纯英文的编码是一样的，无法区分开来，因此通过在字节流最前面添加BOM标记可以告诉软件，当前使用的是Unicode编码，判别成功率就十分准确了。但是需要注意，不是所有软件或者程序都能正确处理BOM标记，例如PHP就不会检测BOM标记，直接把它当普通字节流解析了。因此如果你的PHP文件是采用带BOM标记的UTF-8进行编码的，那么有可能会出现问题。</p><p><strong>Unicode编码和以前的字符集编码有什么区别？</strong></p><p>早期字符编码、字符集和代码页等概念都是表达同一个意思。例如GB2312字符集、GB2312编码，936代码页，实际上说的是同个东西。但是对于Unicode则不同，Unicode字符集只是定义了字符的集合和唯一编号，Unicode编码，则是对UTF-8、UCS-2/UTF-16等具体编码方案的统称而已，并不是具体的编码方案。所以当需要用到字符编码的时候，你可以写gb2312，codepage936，utf-8，utf-16，但请不要写unicode（看过别人在网页的meta标签里头写charset=unicode，有感而发）。</p></p><h2>乱码问题</h2><p>乱码指的是程序显示出来的字符文本无法用任何语言去解读。一般情况下会包含大量?或者?。乱码问题是所有计算机用户或多或少会遇到的问题。<strong>造成乱码的原因就是因为使用了错误的字符编码去解码字节流</strong>，<strong>因此当我们在思考任何跟文本显示有关的问题时，请时刻保持清醒：当前使用的字符编码是什么</strong>。只有这样，我们才能正确分析和处理乱码问题。</p><p>例如最常见的网页乱码问题。如果你是网站技术人员，遇到这样的问题，需要检查以下原因：</p><ul><li>服务器返回的响应头Content-Type没有指明字符编码</li><li>网页内是否使用META HTTP-EQUIV标签指定了字符编码</li><li>网页文件本身存储时使用的字符编码和网页声明的字符编码是否一致</li></ul><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/06/image12.png" width="209" height="121" /> <img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/06/image51.png" width="369" height="66" /></p><p>注意，网页解析的过程如果使用的字符编码不正确，还可能会导致脚本或者样式表出错。具体细节可以参考我以前写过的文章：<a href="http://www.imkevinyang.com/2009/08/%E6%96%87%E6%A1%A3%E5%AD%97%E7%AC%A6%E9%9B%86%E5%AF%BC%E8%87%B4%E7%9A%84%E8%84%9A%E6%9C%AC%E9%94%99%E8%AF%AF.html">文档字符集导致的脚本错误</a>和<a href="http://www.imkevinyang.com/2009/11/asp-net%E9%A1%B5%E9%9D%A2%E7%9A%84%E7%BC%96%E7%A0%81%E9%97%AE%E9%A2%98.html">Asp.Net页面的编码问题</a>。</p><p>不久前看到某技术论坛有人反馈，WinForm程序使用Clipboard类的GetData方法去访问剪切板中的HTML内容时会出现乱码的问题，我估计也是由于WinForm在获取HTML文本的时候没有用对正确的字符编码导致的。Windows剪贴板只支持UTF-8编码，也就是说你传入的文本都会被UTF-8编解码。这样一来，只要两个程序都是调用Windows剪切板API编程的话，那么复制粘贴的过程中不会出现乱码。除非一方在获取到剪贴板数据之后使用了错误的字符编码进行解码，才会得到乱码（我做了简单的WinForm剪切板编程实验，发现GetData使用的是系统默认编码，而不是UTF-8编码）。</p><p>关于乱码中出现?或者?，这里需要额外提一下，<strong>当程序使用特定字符编码解析字节流的时候，一旦遇到无法解析的字节流时，就会用?或者?来替代。因此，一旦你最终解析得到的文本包含这样的字符，而你又无法得到原始字节流的时候，说明正确的信息已经彻底丢失了，尝试任何字符编码都无法从这样的字符文本中还原出正确的信息来</strong>。</p><h2>必要的术语解释</h2><p><strong>字符集（Character Set）</strong>，字面上的理解就是字符的集合，例如ASCII字符集，定义了128个字符；GB2312定义了7445个字符。而<strong>计算机系统中提到的字符集准确来说，指的是已编号的字符的有序集合（不一定是连续）</strong>。</p><p><strong>字符码（Code Point）</strong>指的就是字符集中每个字符的数字编号。例如ASCII字符集用0-127这连续的128个数字分别表示128个字符；GBK字符集使用区位码的方式为每个字符编号，首先定义一个94X94的矩阵，行称为“区”，列称为“位”，然后将所有国标汉字放入矩阵当中，这样每个汉字就可以用唯一的“区位”码来标识了。例如“中”字被放到54区第48位，因此字符码就是5448。而Unicode中将字符集按照一定的类别划分到0~16这17个层面（Planes）中，每个层面中拥有2<sup>16</sup>=65536个字符码，因此Unicode总共拥有的字符码，也即是Unicode的字符空间总共有17*65536=1114112。</p><p><sup></sup></p><p><img style="display: inline" title="image" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/06/image24.png" width="218" height="199" /></p><p><strong>编码</strong>的过程是将字符转换成字节流。</p><p><strong>解码</strong>的过程是将字节流解析为字符。</p><p><strong>字符编码（Character Encoding）</strong>是将字符集中的字符码映射为字节流的一种具体实现方案。例如ASCII字符编码规定使用单字节中低位的7个比特去编码所有的字符。例如‘A’的编号是65，用单字节表示就是0x41，因此写入存储设备的时候就是b’01000001’。GBK编码则是将区位码（GBK的字符码）中的区码和位码的分别加上0xA0（160）的偏移（之所以要加上这样的偏移，主要是为了和ASCII码兼容），例如刚刚提到的“中”字，区位码是5448，十六进制是0x3630，区码和位码分别加上0xA0的偏移之后就得到0xD6D0，这就是“中”字的GBK编码结果。</p><p><strong>代码页（Code Page）</strong>一种字符编码具体形式。早期字符相对少，因此通常会使用类似表格的形式将字符直接映射为字节流，然后通过查表的方式来实现字符的编解码。现代操作系统沿用了这种方式。例如Windows使用936代码页、Mac系统使用EUC-CN代码页实现GBK字符集的编码，名字虽然不一样，但对于同一汉字的编码肯定是一样的。</p><p><strong>大小端</strong>的说法源自《格列佛游记》。我们知道，鸡蛋通常一端大一端小，小人国的人们对于剥蛋壳时应从哪一端开始剥起有着不一样的看法。同样，计算机界对于传输多字节字（由多个字节来共同表示一个数据类型）时，是先传高位字节（大端）还是先传低位字节（小端）也有着不一样的看法，这就是计算机里头大小端模式的由来了。无论是写文件还是网络传输，实际上都是往流设备进行写操作的过程，而且这个写操作是从流的低地址向高地址开始写（这很符合人的习惯），对于多字节字来说，如果先写入高位字节，则称作大端模式。反之则称作小端模式。也就是说，大端模式下，字节序和流设备的地址顺序是相反的，而小端模式则是相同的。一般网络协议都采用大端模式进行传输。</p><p style="text-align: right">——<a title="关于字符编码，你所需要知道的" href="http://www.imkevinyang.com/2010/06/%e5%85%b3%e4%ba%8e%e5%ad%97%e7%ac%a6%e7%bc%96%e7%a0%81%ef%bc%8c%e4%bd%a0%e6%89%80%e9%9c%80%e8%a6%81%e7%9f%a5%e9%81%93%e7%9a%84.html"><em><strong>Kevin Yang</strong></em></a></p><p>参考链接：</p><ul><li><a title="The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)" href="http://www.joelonsoftware.com/printerFriendly/articles/Unicode.html" target="_blank">The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)</a></li><li><a title="http://developers.sun.com/dev/gadc/technicalpublications/articles/gb18030.html" href="http://developers.sun.com/dev/gadc/technicalpublications/articles/gb18030.html">http://developers.sun.com/dev/gadc/technicalpublications/articles/gb18030.html</a></li><li><a title="http://en.wikipedia.org/wiki/Universal_Character_Set" href="http://en.wikipedia.org/wiki/Universal_Character_Set">http://en.wikipedia.org/wiki/Universal_Character_Set</a></li><li><a title="http://en.wikipedia.org/wiki/Code_page" href="http://en.wikipedia.org/wiki/Code_page">http://en.wikipedia.org/wiki/Code_page</a></li></ul>标签：<a href="http://www.imkevinyang.com/tags/ascii" title="ASCII" rel="tag">ASCII</a>, <a href="http://www.imkevinyang.com/tags/gb18030" title="GB18030" rel="tag">GB18030</a>, <a href="http://www.imkevinyang.com/tags/gb2312" title="GB2312" rel="tag">GB2312</a>, <a href="http://www.imkevinyang.com/tags/gbk" title="GBK" rel="tag">GBK</a>, <a href="http://www.imkevinyang.com/tags/oem" title="OEM" rel="tag">OEM</a>, <a href="http://www.imkevinyang.com/tags/unicode" title="Unicode" rel="tag">Unicode</a>, <a href="http://www.imkevinyang.com/tags/utf-16" title="UTF-16" rel="tag">UTF-16</a>, <a href="http://www.imkevinyang.com/tags/utf-8" title="UTF-8" rel="tag">UTF-8</a>, <a href="http://www.imkevinyang.com/categories/techarticles/othertecharticles" title="其他随笔" rel="tag">其他随笔</a>, <a href="http://www.imkevinyang.com/tags/%e5%ad%97%e7%ac%a6" title="字符" rel="tag">字符</a>, <a href="http://www.imkevinyang.com/tags/%e5%ad%97%e7%ac%a6%e7%bc%96%e8%a7%a3%e7%a0%81" title="字符编解码" rel="tag">字符编解码</a>, <a href="http://www.imkevinyang.com/tags/%e7%bc%96%e7%a0%81" title="编码" rel="tag">编码</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2009/04/excel%e4%b8%ad%e4%bd%bf%e7%94%a8vba%e8%87%aa%e5%ae%9a%e4%b9%89%e5%87%bd%e6%95%b0%e5%af%b9%e5%ad%97%e7%ac%a6%e4%b8%b2%e7%bb%a7%e7%bb%adurl%e7%bc%96%e7%a0%81.html" title="Excel中使用VBA自定义函数对字符串进行Url编码（UTF-8） (2009/04/28)">Excel中使用VBA自定义函数对字符串进行Url编码（UTF-8）</a> (2009/04/28)</li><li><a href="http://www.imkevinyang.com/2009/05/php%e5%a4%84%e7%90%86bom%e6%a0%87%e8%ae%b0%e7%9a%84utf-8%e6%96%87%e4%bb%b6%e5%af%bc%e8%87%b4%e7%9a%84%e9%97%ae%e9%a2%98.html" title="PHP处理BOM标记的UTF-8文件导致的问题 (2009/05/05)">PHP处理BOM标记的UTF-8文件导致的问题</a> (2009/05/05)</li><li><a href="http://www.imkevinyang.com/2009/02/%e5%ad%97%e7%ac%a6%e7%bc%96%e8%a7%a3%e7%a0%81%e7%9a%84%e6%95%85%e4%ba%8b%ef%bc%88ascii%ef%bc%8cansi%ef%bc%8cunicode%ef%bc%8cutf-8%e5%8c%ba%e5%88%ab%ef%bc%89.html" title="字符编解码的故事（ASCII，ANSI，Unicode，Utf-8区别） (2009/02/28)">字符编解码的故事（ASCII，ANSI，Unicode，Utf-8区别）</a> (2009/02/28)</li><li><a href="http://www.imkevinyang.com/2009/11/%e5%ad%97%e7%ac%a6%ef%bc%8c%e5%ad%97%e8%8a%82%e5%92%8c%e7%bc%96%e7%a0%81.html" title="字符，字节和编码 (2009/11/27)">字符，字节和编码</a> (2009/11/27)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/06/%e5%85%b3%e4%ba%8e%e5%ad%97%e7%ac%a6%e7%bc%96%e7%a0%81%ef%bc%8c%e4%bd%a0%e6%89%80%e9%9c%80%e8%a6%81%e7%9f%a5%e9%81%93%e7%9a%84.html/feed</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>当Google Analytics、Firefox和IIS走到了一起&#8230;</title><link>http://www.imkevinyang.com/2010/05/%e5%bd%93google-analytics%e3%80%81firefox%e5%92%8ciis%e8%b5%b0%e5%88%b0%e4%ba%86%e4%b8%80%e8%b5%b7.html</link> <comments>http://www.imkevinyang.com/2010/05/%e5%bd%93google-analytics%e3%80%81firefox%e5%92%8ciis%e8%b5%b0%e5%88%b0%e4%ba%86%e4%b8%80%e8%b5%b7.html#comments</comments> <pubDate>Sat, 29 May 2010 10:25:09 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[疑难杂症]]></category> <category><![CDATA[Bad Request]]></category> <category><![CDATA[Cookie]]></category> <category><![CDATA[Fiddler]]></category> <category><![CDATA[Firefox]]></category> <category><![CDATA[Google Analytics]]></category> <category><![CDATA[HTTP]]></category> <category><![CDATA[IE]]></category> <category><![CDATA[IIS]]></category> <category><![CDATA[WFetch]]></category> <category><![CDATA[中文]]></category> <category><![CDATA[非法请求]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/05/%e5%bd%93google-analytics%e3%80%81firefox%e5%92%8ciis%e8%b5%b0%e5%88%b0%e4%ba%86%e4%b8%80%e8%b5%b7.html</guid> <description><![CDATA[<p>今天同事在投放AdWords广告的时候发现了一个诡异的现象：</p><blockquote><p>使用Firefox点击AdWords广告跳转到客户网站上之后，再次刷新页面或者浏览其他页面均提示“Bad Request”的HTTP错误（错误码400）。</p><p>而IE、Chrome下则没有这个问题。</p></blockquote><h2>Cookie惹的祸</h2><p>由于HTTP本身是无状态的，用来实现状态维持的技术一般都是Cookie。而之前我也遇到过几次因为Cookie导致的访问异常。一次是同事用Firefox死活访问不了新东方网站&#8230;</p>]]></description> <content:encoded><![CDATA[<p>今天同事在投放AdWords广告的时候发现了一个诡异的现象：</p><blockquote><p>使用Firefox点击AdWords广告跳转到客户网站上之后，再次刷新页面或者浏览其他页面均提示“Bad Request”的HTTP错误（错误码400）。</p><p>而IE、Chrome下则没有这个问题。</p></blockquote><h2>Cookie惹的祸</h2><p>由于HTTP本身是无状态的，用来实现状态维持的技术一般都是Cookie。而之前我也遇到过几次因为Cookie导致的访问异常。一次是同事用Firefox死活访问不了新东方网站（参见我之前的文章：<a href="http://www.imkevinyang.com/2009/07/firefox%E6%97%A0%E6%B3%95%E8%AE%BF%E9%97%AE%E7%89%B9%E5%AE%9A%E7%BD%91%E7%AB%99.html">Firefox无法访问特定网站</a>），一次是我自己死活登录不了Gmail帐号。这两个问题最终都是以清空Cookie解决的。所以这次有经验了，用web developer bar查看当前客户网站下都有哪些Cookie，一瞄，发现一个带乱码的Cookie。</p><p><img style="display: inline; border-width: 0px;" title="GA的乱码Cookie" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image12.png" border="0" alt="GA的乱码Cookie" width="344" height="33" /></p><p>不用想，也知道这是因为中文没有编码就直接塞到Cookie里头导致的乱码。看看Cookie的来头，__utmz，是Google Analytics（GA）植入的。删除此Cookie之后，访问正常。</p><h2>Google Analytics的Cookie编码问题</h2><p>同事测试的那个广告的Url添加了Google Analytics支持的跟踪参数，并且客户网站上也部署了GA的代码。</p><p>GA在执行时会检测当前Url中是否包含广告跟踪参数（至少必须包含utm_source），一旦发现，则认为是付费流量，这个时候它就会提取广告信息中的来源（utm_source），广告系列（utm_campaign）和广告媒介（utm_medium），对其进行解码（先尝试用decodeURIComponent函数，失败的话再用unescape函数），最后持久化存储到__utmz这个Cookie中。但是就在<strong><span style="color: #008000;">写入Cookie这一步，GA漏掉了编码操作</span></strong>。也就是说，如果我们的广告系列或者广告媒介的原始信息包含中文，那么GA就会直接往Cookie中塞入中文信息。</p><p>举个例子，我要为我的博客投一个宣传广告：</p><ul><li>广告系列：Kevin博客宣传</li><li>广告来源：google</li><li>广告媒介：PPC</li><li>带跟踪参数的着陆页面Url：<a href="http://www.imkevinyang.com/?utm_source=google&amp;utm_medium=ppc&amp;utm_campaign=Kevin%E5%8D%9A%E5%AE%A2%E5%AE%A3%E4%BC%A0">http://www.imkevinyang.com/?utm_source=google&amp;utm_medium=ppc&amp;utm_campaign=Kevin%E5%8D%9A%E5%AE%A2%E5%AE%A3%E4%BC%A0</a></li></ul><p>那么GA在写Cookie的时候，会执行类似下面的代码（当然这里简化了__utmz的值）：</p><pre class="brush: js">var data = "Kevin博客宣传";
// GA错误的Cookie操作
document.cookie = "_utmz=" + data;
// 正确的Cookie存储操作
document.cookie = "_utmz=" + encodeURI(data);</pre><p>使用Javascript对Cookie进行存取，标准的操作应该是在存入的时候编一次码，取出的时候解一次码。这样保证存放在Cookie中的都是ASCII字符。早期JS使用escape/unescape进行编解码，现在通常使用encodeURI或者encodeURIComponent函数，这两个函数用的都是UTF-8编码。</p><h2>中文Cookie潜在的问题</h2><p>那么当我们直接将中文直接存到Cookie又会发生了什么事呢？IE和Firefox的行为有什么不一样的地方呢？我们在IE8和Firefox3.6下做几个实验。</p><h3>IE8对中文Cookie的处理</h3><p>实验步骤：</p><ul><li>打开IE8，清空所有Cookie和缓存，建立干净的测试环境。</li><li>访问<a href="http://www.imkevinyang.com/">http://www.imkevinyang.com/</a></li><li>地址栏执行javascript:alert(document.cookie="mycookie=缂栫爜编码;expires=Mon, 25 May 2020 10:31:49 GMT")，写入一个持久化cookie。</li></ul><p>这样就在我的博客上设置了一个2020年5月25号过期的cookie了。之所以要设置持久化cookie而不是会话Cookie，是因为IE会将持久化Cookie写入到硬盘上了，这样方便我们了解这个过程，而会话cookie我目前还不清楚他存储的位置。</p><p>细心的你会注意到，上面这个cookie的值很奇怪，有几个乱码。其实那段乱码是我把“编码”这两个汉字的UTF-8编码（6个字节）使用GB2312解码（每两个字节对应一个字符）后得到的字符。至于为什么要这样测试，一会我们就会知道了。</p><p>IE地址栏用的是ANSI编码，也就是说当你在地址栏输入中文的时候，IE会将中文字符以系统默认字符集进行编码。当你使用中文系统时，地址栏的“编码”字符，实际上最后会被编码为B1 E0 C2 EB四个字节，而在英文系统下，系统使用的是西方字符集作为默认字符集，没有中文字符，因此“编码”这两个字符会被替换成?，也就是3F。</p><p>IE在创建cookie文件，会自动选择最合适的编码。当我们写入“缂栫爜编码”（GB2312编码后得到二进制流E7 BC 96 E7 A0 81 B1 E0 C2 EB），由于最后四个字节无法用UTF-8解码，因此IE会将文件存储为GB2312。（如果你只测试“缂栫爜”的话，IE会将文件存储为UTF-8）。</p><p>好了，现在让我们来看看文件里头都是什么内容。</p><p>打开everything工具，搜索“www.imkevinyang txt”这样就会列出文件名包含<a href="http://www.imkevinyang">www.imkevinyang</a>和txt的所有文件。</p><p><img style="display: inline; border-width: 0px;" title="Everything快速搜索" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image13.png" border="0" alt="Everything快速搜索" width="158" height="84" /></p><p>打开这个文件，里头存放的就是IE持久化的cookie信息。</p><p><img style="display: inline; border-width: 0px;" title="IE存储持久化Cookie的文件" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image14.png" border="0" alt="IE存储持久化Cookie的文件" width="144" height="164" /> <img style="display: inline; border-width: 0px;" title="IE存储持久化Cookie的文件——二进制形式" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image15.png" border="0" alt="IE存储持久化Cookie的文件——二进制形式" width="406" height="105" /></p><p>这个时候，我们再在地址栏通过javascript:alert(document.cookie)我们会发现，IE显示的Cookie值和我们一开始设置的是一样的。</p><p>看完了本地的Cookie信息，我们接下来看看IE发送给服务器的Cookie又是什么。</p><p>我们用Fiddler来监视整个HTTP通讯过程（这里不用HTTP Watch是因为HTTP Watch会将HTTP消息解码后显示出来，没办法看到原始二进制数据，不方便分析）。</p><p>我们再向我的博客首页发起一次访问，在Fiddler中我们会看到：</p><p>（文本形式）</p><p><img style="display: inline; border-width: 0px;" title="Fiddler观察发送中文Cookie（文本形式）" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image16.png" border="0" alt="Fiddler观察发送中文Cookie（文本形式）" width="477" height="185" /></p><p>（二进制原始数据）</p><p><img style="display: inline; border-width: 0px;" title="Fiddler观察发送中文Cookie（二进制形式）" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image17.png" border="0" alt="Fiddler观察发送中文Cookie（二进制形式）" width="474" height="198" /></p><p>我们很惊奇的看到，IE发送的并不是我们设置的那些字符“缂栫爜编码”（二进制是E7 BC 96 E7 A0 81 B1 E0 C2 EB），而是“编码����”（现在知道我为什么要用“缂栫爜编码”做测试了把）。对应的二进制是E7 BC 96 E7 A0 81 EF BF BD EF BF BD EF BF BD EF BF BD。注意到，IE将原始信息的后面4个字节替换成了EF BF BD.</p><p>这是因为IE发送HTTP消息的时候会检测字节流是否是能够以UTF-8解码，如果不行，那么会将相应的异常字节替换成EF BF BD（也就是对应�字符）。这有点类似于我们之前提到的，英文系统对于缺失的字符会使用?号替代。</p><h3>Firefox对于中文Cookie的处理</h3><p>Firefox不像IE那样把Cookie直接存储为文件的形式，所以我们研究起来没那么方便。</p><p>不过我们还是按照上面同样的步骤来做实验，不过这次为了简单起见我们修改一下测试的Cookie值。</p><ul><li>打开Firefox，清空所有Cookie和缓存，建立干净的测试环境。</li><li>访问<a href="http://www.imkevinyang.com/">http://www.imkevinyang.com/</a></li><li>地址栏执行javascript:alert(document.cookie="mycookie=1编码1")</li></ul><ul>第一次Firefox弹出的对话框显示我们Cookie应该是设置成功了，返回“1编码1”字符串。</ul><p><img style="display: inline; border: 0px;" title="Firefox设置Cookie" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image18.png" border="0" alt="Firefox设置Cookie" width="297" height="138" /></p><p>但如果你再次通过Javascript:alert(document.cookie)你会发现，这次弹出的内容变了：</p><p><img style="display: inline; border: 0px;" title="Firefox显示乱码的Cookie" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image19.png" border="0" alt="Firefox显示乱码的Cookie" width="325" height="117" /></p><p>我们通过Web Developer Toolbar查看当前域下的Cookie，发现，目前的Cookie确实是像上面第二个对话框所示的，是带乱码的：</p><p><img style="display: inline; border: 0px;" title="Web Developer Bar看到的乱码的Cookie" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image20.png" border="0" alt="Web Developer Bar看到的乱码的Cookie" width="222" height="188" /></p><p>我们现在关心的问题是，这个乱码是怎么来的？</p><p>我们先把这串文字拷贝到Notepad++中（注意，需要将Notepad++调到UCS-2编码状态下）看一下对应的字节是什么。</p><p><img style="display: inline; border: 0px;" title="乱码cookie的二进制" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image21.png" border="0" alt="乱码cookie的二进制" width="239" height="41" /></p><p>31是字符“1”的ASCII码。而16和01是哪来的呢？</p><p>其实是Unicode Code Point。“编码”的Unicode码是“7F16 7801”。上面显示的16和01就是截断了Unicode码高位得到的。为了证实这个结论，我又测试了好几个中文cookie，均是如此。</p><p>也就是说，Firefox的地址栏使用的是Unicode码，也就是说当你输入“mycookie=1编码1”这样的字符串的时候，Firefox看到的是：</p><p>\u006d\u0079\u0063\u006f\u006f\u006b\u0069\u0065\u003d\u0031\u7f16\u7801\u0031</p><p>在存储中文Cookie的时候，他会将Unicode的高位截断，保留低位。然后写入Cookie存储。这也是为什么我们会看到“编码”这个Cookie变成了“16 01”。</p><p>Firefox向服务端发送HTTP请求时对于http消息的编码处理方式和IE的一样，也是判断字节流能够以UTF-8进行解码，这里就不再赘述了。有兴趣的朋友可以按照上面的方法去测试。</p><h2>为什么Firefox无法访问</h2><p>基于上面对IE和Firefox对中文Cookie的处理方式的了解，我们现在可以知道，对于中文Cookie，IE是用ANSI编码，也就是说Cookie中永远不会出现ASCII字符集中的不可打印字符（GB2312编码每个字节也都是从A0开始的），而Firefox采用Unicode码，却又对其进行了高位截断，导致Cookie有可能会出现ASCII字符集中的非打印字符。</p><p>IE和Firefox在构造HTTP消息的时候对于字节流序列编码问题的处理方式一样。无法使用UTF-8解码的字节流序列，将其替换成EF BF BD，这个我们在Fiddler中已经看到了。而对于ASCII字符集的非打印字符则不做任何处理，直接发送到服务器端。</p><p>所以用Firefox访问，服务端收到的HTTP Request有可能包含非打印字符，而IE访问的话，则不会出现这样的情况。</p><p>例如Firefox上设置了一个中文Cookie，“我”，Unicode码是62 11，被Firefox高位截断了，就剩下11了，对应着ASCII码表中的Device Control 1，也就是控制字符。那么当你带着这个Cookie向服务端发起请求的时候服务端有可能就会直接抛出Bad Request的异常，告诉客户端，你发过来的请求不符合HTTP规范。</p><p>所以实际上不只是Cookie不能出现这样的非打印字符，其他HTTP Header中也不能出现这样的非打印字符。我们可以直接使用WFetch来构造这样的“非法”请求：</p><p><img style="display: inline; border: 0px;" title="Wfetch发送异常请求" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image22.png" border="0" alt="Wfetch发送异常请求" width="177" height="183" /> <img style="display: inline; border: 0px;" title="UserAgent中包含非打印字符" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image23.png" border="0" alt="UserAgent中包含非打印字符" width="283" height="164" /></p><p>服务端一样会抛出400 Bad Request。</p><h2>IIS和Apache的不同处理方式</h2><p>当客户端发起的请求存在问题时，服务端的处理方式是取决于不同服务器的实现的。我们上面讨论的这个问题，实际上只会对IIS造成影响，对那些后台采用Apache或者LiteSpeed这类的服务器不会有影响。这说明IIS的容错性还是稍微差一点，不知道从安全的角度来考虑是好事还是坏事。</p><h2>总结回顾</h2><p>上面讲了那么多，你可能听着有点乱了。我们重新来整理一遍整个故事。</p><blockquote><p>广告代理商投了一个广告，着陆页面Url中添加了google的广告参数，其中带有中文信息，客户网站上部署了GA代码，GA读取到此中文信息之后直接扔到Cookie中而没有经过编码。Firefox内部将此中文的Unicode码高位截断保留低位存下来。当你再次刷新页面的时候，Firefox把这个截断的字符发给IIS服务器，而刚好这个截断之后的字符是一个非打印字符，IIS觉得自己无法处理，就抛出一个Bad Request，告诉客户端此请求非法，我无法处理。</p></blockquote><p>整个故事就这样。</p><p>怎么办呢？建议为了保险起见，如果客户网站服务器用的是IIS，那么你还是不要在Firefox上投放那些Url跟踪参数带中文（即使是UTF-8编码过）的广告了，否则可能浪费钱，因为用户来了，再点一次可能就无法访问了，而且以后可能都无法访问了（现在终于知道为什么我那同事当时用Firefox始终访问不了新东方了...）。(<span style="color: #ff0000;">update:2010-7-2</span>)或者你在投放广告的时候，Url参数中的广告系列、广告媒介以及广告来源这三个跟踪参数不要包含中文信息（即使是UTF-8编码过的），全部使用英文，这样也不会有问题。</p><p>希望整个分析过程对你有所帮助。</p><p style="text-align: right;">——<a title="当Google Analytics、Firefox和IIS走到了一起..." href="http://www.imkevinyang.com/2010/05/%e5%bd%93Google%20Analytics%e3%80%81Firefox%e5%92%8cIIS%e8%b5%b0%e5%88%b0%e4%ba%86%e4%b8%80%e8%b5%b7....html"><em><strong>Kevin Yang</strong></em></a></p>标签：<a href="http://www.imkevinyang.com/tags/bad-request" title="Bad Request" rel="tag">Bad Request</a>, <a href="http://www.imkevinyang.com/tags/cookie" title="Cookie" rel="tag">Cookie</a>, <a href="http://www.imkevinyang.com/tags/fiddler" title="Fiddler" rel="tag">Fiddler</a>, <a href="http://www.imkevinyang.com/tags/firefox" title="Firefox" rel="tag">Firefox</a>, <a href="http://www.imkevinyang.com/tags/google-analytics" title="Google Analytics" rel="tag">Google Analytics</a>, <a href="http://www.imkevinyang.com/tags/http" title="HTTP" rel="tag">HTTP</a>, <a href="http://www.imkevinyang.com/tags/ie" title="IE" rel="tag">IE</a>, <a href="http://www.imkevinyang.com/tags/iis" title="IIS" rel="tag">IIS</a>, <a href="http://www.imkevinyang.com/tags/wfetch" title="WFetch" rel="tag">WFetch</a>, <a href="http://www.imkevinyang.com/tags/%e4%b8%ad%e6%96%87" title="中文" rel="tag">中文</a>, <a href="http://www.imkevinyang.com/categories/techarticles/knottyproblems" title="疑难杂症" rel="tag">疑难杂症</a>, <a href="http://www.imkevinyang.com/tags/%e9%9d%9e%e6%b3%95%e8%af%b7%e6%b1%82" title="非法请求" rel="tag">非法请求</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2010/05/64%e4%bd%8d%e7%b3%bb%e7%bb%9f%e4%b8%8biis7-isapi%e5%a4%84%e7%90%86%e5%99%a8%e5%8a%a0%e8%bd%bd%e5%a4%b1%e8%b4%a5.html" title="64位系统下IIS7 ISAPI处理器加载失败 (2010/05/05)">64位系统下IIS7 ISAPI处理器加载失败</a> (2010/05/05)</li><li><a href="http://www.imkevinyang.com/2010/01/document-referrer%e4%b8%a2%e5%a4%b1%e7%9a%84%e5%87%a0%e4%b8%aa%e5%8e%9f%e5%9b%a0.html" title="Document.Referrer丢失的几个原因 (2010/01/18)">Document.Referrer丢失的几个原因</a> (2010/01/18)</li><li><a href="http://www.imkevinyang.com/2009/07/firefox%e6%97%a0%e6%b3%95%e8%ae%bf%e9%97%ae%e7%89%b9%e5%ae%9a%e7%bd%91%e7%ab%99.html" title="Firefox无法访问特定网站 (2009/07/02)">Firefox无法访问特定网站</a> (2009/07/02)</li><li><a href="http://www.imkevinyang.com/2010/01/google-analytics%e4%b8%ad%e7%9a%84page%e7%bb%b4%e5%ba%a6.html" title="Google Analytics中的Page维度 (2010/01/04)">Google Analytics中的Page维度</a> (2010/01/04)</li><li><a href="http://www.imkevinyang.com/2009/03/ie%e4%b8%ad%e4%bd%bf%e7%94%a8windowopen%e6%89%93%e5%bc%80%e6%96%b0%e7%aa%97%e5%8f%a3%e6%97%b6%e6%97%a0%e6%b3%95%e8%8e%b7%e5%8f%96referrer%e5%af%b9%e8%b1%a1.html" title="IE中使用window.open打开新窗口时无法获取Referrer对象 (2009/03/07)">IE中使用window.open打开新窗口时无法获取Referrer对象</a> (2009/03/07)</li><li><a href="http://www.imkevinyang.com/2009/06/javascript%e6%93%8d%e7%ba%b5cookie.html" title="Javascript操纵Cookie (2009/06/11)">Javascript操纵Cookie</a> (2009/06/11)</li><li><a href="http://www.imkevinyang.com/2010/02/silverlight%e5%90%af%e7%94%a8assembly-caching%e4%b9%8b%e5%90%8e%e9%93%81%e9%80%9a%e7%94%a8%e6%88%b7%e6%97%a0%e6%b3%95%e8%ae%bf%e9%97%ae.html" title="Silverlight启用Assembly Caching之后铁通用户无法访问 (2010/02/13)">Silverlight启用Assembly Caching之后铁通用户无法访问</a> (2010/02/13)</li><li><a href="http://www.imkevinyang.com/2009/11/%e3%80%90%e6%8e%a8%e8%8d%90%e3%80%91%e4%b8%a4%e6%ac%behttp%e6%b5%81%e9%87%8f%e5%88%86%e6%9e%90%e5%b7%a5%e5%85%b7%e7%9a%84%e6%af%94%e8%be%83.html" title="【推荐】两款HTTP流量分析工具的比较 (2009/11/08)">【推荐】两款HTTP流量分析工具的比较</a> (2009/11/08)</li><li><a href="http://www.imkevinyang.com/2009/09/%e4%bd%bf%e7%94%a8%e7%9b%b8%e5%af%b9url%e6%97%a0%e7%bc%9d%e5%88%87%e6%8d%a2http-https.html" title="使用相对Url无缝切换HTTP-HTTPS (2009/09/18)">使用相对Url无缝切换HTTP-HTTPS</a> (2009/09/18)</li><li><a href="http://www.imkevinyang.com/2010/02/%e5%bd%93%e5%89%8d%e6%97%a5%e6%9c%9f110%e5%b9%b4.html" title="当前日期110年 (2010/02/12)">当前日期110年</a> (2010/02/12)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/05/%e5%bd%93google-analytics%e3%80%81firefox%e5%92%8ciis%e8%b5%b0%e5%88%b0%e4%ba%86%e4%b8%80%e8%b5%b7.html/feed</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>SSIS调用存储过程失败</title><link>http://www.imkevinyang.com/2010/05/ssis%e8%b0%83%e7%94%a8%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e5%a4%b1%e8%b4%a5.html</link> <comments>http://www.imkevinyang.com/2010/05/ssis%e8%b0%83%e7%94%a8%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e5%a4%b1%e8%b4%a5.html#comments</comments> <pubDate>Sun, 23 May 2010 07:02:47 +0000</pubDate> <dc:creator>Kevin Yang</dc:creator> <category><![CDATA[疑难杂症]]></category> <category><![CDATA[charindex]]></category> <category><![CDATA[SQL]]></category> <category><![CDATA[SQL Server]]></category> <category><![CDATA[SSIS]]></category> <category><![CDATA[substring]]></category> <category><![CDATA[存储过程]]></category> <category><![CDATA[校验失败]]></category><guid isPermaLink="false">http://www.imkevinyang.com/2010/05/ssis%e8%b0%83%e7%94%a8%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e5%a4%b1%e8%b4%a5.html</guid> <description><![CDATA[<p>最近更新了一个Sql存储过程的实现，结果导致做ETL的SSIS（Sql Server Integration Services）包出现了问题。在调试状态下发现，SSIS在启动时对包做校验的过程中抛出了异常，显示我修改后的这个存储过程有问题。</p><p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image10.png" width="558" height="66" /></p><blockquote><p>Invalid length parameter passed to the substring or left function.</p></blockquote><p>奇怪的是，这还不是调用的时候抛出了异常，而是在校验阶段（Validation Stage）就抛&#8230;</p>]]></description> <content:encoded><![CDATA[<p>最近更新了一个Sql存储过程的实现，结果导致做ETL的SSIS（Sql Server Integration Services）包出现了问题。在调试状态下发现，SSIS在启动时对包做校验的过程中抛出了异常，显示我修改后的这个存储过程有问题。</p><p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2010/05/image10.png" width="558" height="66" /></p><blockquote><p>Invalid length parameter passed to the substring or left function.</p></blockquote><p>奇怪的是，这还不是调用的时候抛出了异常，而是在校验阶段（Validation Stage）就抛出了异常。我感到纳闷，难道SSIS或者Sql Server还能对我的存储过程作单元测试？？</p><p>用这句错误信息上Google搜了一下，发现问题是出在CharIndex函数和Substring函数的联合调用上。为了简化，我下面只摘取了一段出问题的代码：</p><pre class="brush: sql">declare @index int
set @index = charindex('/',@url,1)
set @url = substring(@url,1,@index - 1)</pre></p><p>以前的VBA函数还有Sql函数都喜欢使用1作为数组起始索引，用0表示返回失败，而不像现在C风格下使用0作为起始索引，-1作为失败返回值。charindex这个sql函数是用来查找某个字符串在特定字符串中的位置，如果找不着匹配的，则返回0。而下一行代码中的substring又传入了一个@index-1的参数，因此此参数有可能为负数。猜测就是因为这样，所以SSIS检测失败，抛出异常。之所以当时这么写，是因为基于一个假设，就是业务系统中，此字段都会包含/字符。但是人家SSIS组件可不认你的假设。（再一次证明了，编程的时候假设越少越好）</p><p>既然如此，我们就多加一层检测。修改代码如下：</p><pre class="brush: sql">declare @index int
set @index = charindex('/',@url,1)
if(@index &gt; 0)
  set @url = substring(@url,1,@index - 1)</pre><p>启动SSIS包，结果还是抛出一样的错误。这就奇怪了。难道做了检测还不行？难道substring的第三个参数非得是大于0的？试了一下，改成if(@index &gt; 1000)都不行。</p><p>想了半天，最终我只能怀疑，SSIS的这个校验组件，是通过判断变量什么时候被赋值来获得值的可能范围是多少，而不会判断上下文是否有对变量做有效性检测。也就是说，在上面这个存储过程中，@index变量只在charindex函数返回时被赋值，值范围是0~正无穷。因此传给substring的第三个参数的时候，@index – 1的值范围就变成了-1~正无穷了。它直接无视我上面的if语句了。</p><p>把这个猜想和同事讨论了一下，他设计了一个验证方案，就是添加一行永远不会被执行的的赋值语句。代码如下：</p><pre class="brush: sql">declare @index int
set @index = charindex('/',@url,1)
if(@index &gt; 0)
begin
  if(1=0) set @index = 1000
  set @url = substring(@url,1,@index - 1)
end</pre><p>注意上面代码中将@index赋值为1000的操作，是不会被执行的。可是却通过了SSIS的校验。这说明，SSIS的校验组件做的工作实际上是非常简单的校验而已，不会参考上下文代码。</p><p>既然如此，这个校验对于开发人员来说，不但没有意义，而且还会影响我们编写逻辑正确的代码。因为这个校验是不能被关闭的。也就是说，为了绕过这个没意义而且错误的校验，我不得不在charindex和substring这种联合调用的代码中加入一行永远不会被执行的赋值语句来迷惑SSIS的校验组件。否则SSIS的包根本启动不了。</p><p>感觉这应该是一个设计上的Bug。</p><p>希望对遇到这个问题的人有所帮助。</p><p style="text-align: right">——<a title="SSIS调用存储过程失败" href="http://www.imkevinyang.com/2010/05/SSIS%e8%b0%83%e7%94%a8%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e5%a4%b1%e8%b4%a5.html"><em><strong>Kevin Yang</strong></em></a></p>标签：<a href="http://www.imkevinyang.com/tags/charindex" title="charindex" rel="tag">charindex</a>, <a href="http://www.imkevinyang.com/tags/sql" title="SQL" rel="tag">SQL</a>, <a href="http://www.imkevinyang.com/tags/sql-server" title="SQL Server" rel="tag">SQL Server</a>, <a href="http://www.imkevinyang.com/tags/ssis" title="SSIS" rel="tag">SSIS</a>, <a href="http://www.imkevinyang.com/tags/substring" title="substring" rel="tag">substring</a>, <a href="http://www.imkevinyang.com/tags/%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b" title="存储过程" rel="tag">存储过程</a>, <a href="http://www.imkevinyang.com/tags/%e6%a0%a1%e9%aa%8c%e5%a4%b1%e8%b4%a5" title="校验失败" rel="tag">校验失败</a>, <a href="http://www.imkevinyang.com/categories/techarticles/knottyproblems" title="疑难杂症" rel="tag">疑难杂症</a><br /><h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4><ul class="st-related-posts"><li><a href="http://www.imkevinyang.com/2009/05/sql-server%e4%b8%ad%e4%bf%ae%e6%94%b9%e8%87%aa%e5%a2%9e%e9%95%bf%e5%88%97%e7%bb%8f%e5%b8%b8%e9%9c%80%e8%a6%81%e7%94%a8%e5%88%b0%e7%9a%84%e9%85%8d%e7%bd%ae.html" title="Sql Server中修改自增长列经常需要用到的配置 (2009/05/24)">Sql Server中修改自增长列经常需要用到的配置</a> (2009/05/24)</li><li><a href="http://www.imkevinyang.com/2010/07/sql-tipsupdate%e8%af%ad%e5%8f%a5%e4%b9%9f%e4%bd%bf%e7%94%a8%e8%a1%a8%e5%88%ab%e5%90%8dtable-alias.html" title="Sql Tips——Update语句也使用表别名(Table Alias) (2010/07/02)">Sql Tips——Update语句也使用表别名(Table Alias)</a> (2010/07/02)</li><li><a href="http://www.imkevinyang.com/2009/10/%e5%85%a8%e5%8d%8a%e8%a7%92%e7%a9%ba%e6%a0%bc%e5%af%bc%e8%87%b4%e7%9a%84analysis-service%e5%a4%84%e7%90%86%e9%94%99%e8%af%af.html" title="全半角空格导致的Analysis Services处理错误 (2009/10/09)">全半角空格导致的Analysis Services处理错误</a> (2009/10/09)</li><li><a href="http://www.imkevinyang.com/2009/09/%e9%87%8a%e6%94%besql-server%e5%8d%a0%e7%94%a8%e7%9a%84%e5%86%85%e5%ad%98.html" title="释放SQL Server占用的内存 (2009/09/01)">释放SQL Server占用的内存</a> (2009/09/01)</li></ul>]]></content:encoded> <wfw:commentRss>http://www.imkevinyang.com/2010/05/ssis%e8%b0%83%e7%94%a8%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e5%a4%b1%e8%b4%a5.html/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> </channel> </rss>