Archive for February, 2007

寒假结束
Sunday, February 25th, 2007

弟弟先我一天去上学,我在家也倍感无聊。周围都是小学同学,大都有了孩子,谈话没有共同语言了。

昨天早上6:30就起床,坐车到县城。在车站看到红旗招展人山人海在等到北京的直达车,想都没想就买了到保定的票,打算从保定转车到北京。在保定站,发现等车的人更多了,被人带到个大院子里,一队人密密麻麻围了整整一圈,我们排在末尾。看见对面有个拐角,人们从那里拐出去排着队,就奇怪,拐个小弯也多放不了几个人啊。后来排到那边才发现,那里拐到了一个更大的院子里,也被人密密麻麻围了一圈,每个人刚到那里都傻眼了。排队途中不停地有开黑车的动摇人们排队的信心,让人坐他们的车。算起来,至少有一千人吧,好在车多,排了不到一小时就上了车,下午五点钟左右到了庄严肃穆的保福寺。在门口要了一笼包子,一碗面,想大吃一顿,结果吃面的时候看到了小强的尸体,真够倒霉的。

到了宿舍,才发现是星期六,来得太早了。

坐车接近北京的时候就感觉到在这种城市中有着和农村不一样的压力,还得拼搏。

做人要厚道
Sunday, February 11th, 2007

至少可以说,作为一个有着中华民族传统美德的中国人来说,厚道是应该的。

各位看一下这篇日志

下面有个愤青的回复也要注意一下,也许这人就是yobo的人。竟然谈起美国人欺骗世界,真可笑。典型的阿Q思想活学活用。人类在进步,我们的社会形态已经叫做社会主义了,理论上是比资本主义先进的,你还在学习资本主义初期的东西,赶紧回家学习一下三个代表重要思想。

你一个人无耻不要紧,为什么这么多人想骂呢?中国人的形象全毁在你这样人的手里了。

一个朋友刚刚说,中国人(一部分)太善于欺骗了。他们不是看到是外国人就欺骗他,而是把对付自己兄弟姐妹的伎俩偶尔用在了外国人的身上。这才是真正的悲哀。

用密码保护整个blog
Friday, February 9th, 2007

WordPress 只提供对某篇文章进行密码保护,但它的标题仍然显示。如果你的Blog是写日记,可能什么都不想让外人看到。

WP支持论坛的一个主题中给出一种非常简单的解决方案,未登录用户自动被重定向到登录页面。将以下代码复制到你的Blog根目录下,并将$siteurl变量赋予正确的值(从论坛里直接复制的代码似乎有问题)。

<?php
$siteurl = 'http://your.blog.url';

$cookiehash = md5($siteurl);
if(empty($_COOKIE['wordpressuser_' . $cookiehash])) :
header(’Location: /wp-login.php’);
else :
define(’WP_USE_THEMES’, true);
require(’./wp-blog-header.php’);
endif;
?>

刚才朋友让我给他弄密码保护,我把自己的改了一下,然后告诉他好了。刚才上自己Blog出了问题,才想起来改错了。。。我说怎么好几个小时没收到垃圾评论呢,看来对付spam最好的办法还是──关站。

GBK(GB2312)向UTF-8的编码转换
Thursday, February 8th, 2007

最近做一个IE插件,要从网页中取得文字,编码到一个URL中去。在前一篇文章“中文URL编码”中,粗略地介绍了URL编码的规则,以及中文URL编码的过程,但在如何将GBK或者GB2312编码的汉字转换到UTF-8编码仍然是一个问题。编码是一个很复杂的问题,我也了解甚少,这里只是写写我的经验,欢迎补充和指正。

在PHP、.NET中,编码的转换都比较容易。ATL中有一些宏是用来做编码转换的,我没试过,而且我更愿意用后面所讲的方法。

在COM编程中,字符串多存储在BSTR结构中。网上许多文章都说这个数据结构中存储的就是Unicode,我就试了好多次从Unicode转UTF-8,未遂。在Debug的时候,含有中文字符串的BSTR能够正常显示,说明它的编码应该是GBK.

如何从GBK转换到UTF-8呢?libiconv应该可以做到,然而我使用它的Windows port后,可以编译、注册COM组件,就是工具栏出不来了,于是放弃。上网搜索,得到一个被广泛转载的CChineseCode类。然而它仅仅针对汉字(每个汉字在UTF-8编码中占3个字节),如果字符串中有英文,就有麻烦了,因为英文在UTF-8编码中只有一个字节。另外有的字符会占用更多的字节。所以这个类并不适用。

正确的方法是用Win32 API的MultiByteToWideCharWideCharToMultiByte两个函数,Wide character指的就是Unicode. GBK和UTF-8之间的转换,需要用Unicode作为桥梁(在这种方法里)。比如我们要转换这样一个字符串”编码 - Google 搜索”。

从GBK向Unicode转换

该字符串在BSTR类型的变量in中存储,首先将其转换为普通的字符串:

char *lpszText = _com_util::ConvertBSTRToString(in);

此时,如果用strlen函数取得lpszText的长度,则为18,4个汉字,每个占两个字节,另外有10个英文字符。所以说GBK/GB2312是MultiByte而不是WideChar. 并且有lpszText[0] == 0xb1 && lpszText[1] == 0xe0,在微软Windows Codepage 936这一页上查到果然是“编”字,更坚定了我们认为它是GBK的信心。

转换到Unicode所用的函数是MultiByteToWideChar,第一个参数是MultiByte的Code page,如果确定是GBK,就可以使用936. 我考虑它应该是与系统有关的(比如日语系统上应该是932),所以使用CP_ACP,系统所用的Codepage.

先通过将cchWideChar参数设置为0,取得转换后需要的空间大小,然后分配空间,再做实际的转换(转换时cbMultiByte为-1表示要转换的字符串以0结尾)。代码如下:

int wLen = MultiByteToWideChar(CP_ACP, 0, lpszText, -1, NULL, 0);
LPWSTR wStr = (LPWSTR)CoTaskMemAlloc(wLen * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, lpszText, -1, wStr, wLen);

wLen是15,注意是指宽字符的个数,很贴心,14个字符,加上末尾的结束符。分配空间的时候也要注意,不是15个字节,而应该分配30个字节。这些在MSDN中都有说明,仔细看cchWideChar参数的介绍。最后一行代码执行后,wStr中就是这些汉字的Unicode了,查看一下,wStr[0] == 0×7f16,刚才在微软Windows Codepage 936查找时,“编”字的下面标明7f16,就是它的Unicode编码,说明一切正常。

从Unicode向UTF-8转换

转换到Unicode后,就可以使用WideCharToMultiByte函数将其转换到UTF-8编码,这次的code page要用CP_UTF8. 和前面的转换一样,先计算所需要的空间大小并分配,再做实际转换。

int aLen = WideCharToMultiByte(CP_UTF8, 0, wStr, -1, NULL, 0, NULL, NULL);
char* converted = (char*)CoTaskMemAlloc(aLen);
WideCharToMultiByte(CP_UTF8, 0, wStr, -1, converted, aLen, NULL, NULL);

aLen为23,因为4个汉字,每个占3个字节,加上10个英文字符(每个占1字节),再加末尾的’\0′,正好是23. 现在converted里就是字符串”编码 - Google 搜索”的UTF-8编码。converted[0] == 0xe7 && converted[1] == 0xbc,正是“编”字的UTF-8编码。

好了,现在终于得到了中英文混合字符串的UTF-8字节序列,可以进行URL编码(percent encoding)了。

如果你也看了CChineseCode类的代码,就会奇怪既然作者知道用WideCharToMultiByte做GB2312到Unicode的转换,为什么在UnicodeToUTF_8函数中要舍近求远呢?

中文URL编码
Saturday, February 3rd, 2007

URL编码是一个比较麻烦的事情,RFC 3986是关于URI的一个标准,在它的第2节定义了字符如何在URI中进行表示,而第3节把一个URI区分为scheme, hier-part, query, fragment几个component。根据这个RFC,A URI is composed from a limited set of characters consisting of digits, letters, and a few graphic symbols. 附录A中给出了ABNF定义。

比如URL,http://www.qingbo.org/?p=230#comments,就包含了上述的全部四个components。这个URL不需要进行百分号编码,因为每个component中都没有保留字,全部为字母、数字或者非保留的ASCII可见字符(见RFC 3986第2.3节)。

假设我们在Firefox中打开了这个URL,并且想通过del.icio.us的插件按钮把它收藏起来。del.icio.us插件打开一个新窗口,并通过GET方法向服务器发出请求,将这个URL和它对应的title作为query的参数传递给服务器,服务器就把这两个值填充到对应的input value属性中。

假如不经过编码,这个GET请求中的URL就是”http://del.icio.us/flimsy?url=http://www.qingbo.org/?p=230#comments&title=星月轮转 » Blog Archive » Blog换了个样子&noui&jump=close&v=4″。问题就出来了,#号后面的是什么呢?按理说应该解释为页面中的一个anchor,然而#comments只是url这个参数的一部分。另外,URL中含有汉字,也不符合标准。所以编码是必须的,要针对各个component,以及query中的每个参数值做percent encoding. 注意不是对整个URL(指del.icio.us的GET请求中的长URL)做,如果flimsy后面的问号被编码,服务器便不知道它后面是query部分了。正确编码后的链接应该是这样,很长,就不显示了,你可以复制链接地址来看看(似乎显示的时候浏览器又自动decode了,点击即可在地址栏看到编码后结果)

URL编码要求先将URL转换为一个UTF-8的字节序列,然后再做percent encoding, 这在RFC 3986和W3C的网站上都有介绍。在为Firefox做插件时,主要用Javascript实现程序逻辑,而Javascript中的字符串是UTF-8编码,且有一个方便的encodeURIComponent函数可以做URL编码。除此之外,还有escape/encodeURI两个函数。关于它们三个的比较,可参考这篇文章

如果没有现成的函数,对UTF-8字节序列进行percent encoding也是比较方便的。对于unreserved character值,不需要做转换,其它的字节全部用% HEXDIG HEXDIG来表示。另外空格除”%20″外,也可以转为”+”,节省空间。

一个非常重要的问题就是如何把字符(ASCII字符除外,主要是中文等)转换为UTF-8的字节序列。并不是每种语言都像Javascript那样方便,比如在C++中,你得到了一个含有中文的宽字符串,怎么处理呢?Win32 API中是有一个InternetCanonicalizeUrl函数,但它只针对一个字节序列,不会考虑中文的编码转换。在MSDN“规范的URL”定义中,”Characters that must be encoded”并没有提到宽字符的处理,甚至没有提到UTF-8. 它还要求字符串中必须包含一个scheme.

有时间的时候我会再写一篇关于怎么在Windows中把中文转换为UTF-8字节序列的文章,对进行中文URL编码的朋友也许会有帮助。见“GBK(GB2312)向UTF-8的编码转换”一文。

PS. 网上文章一大抄,这几天体会深刻,中文文章尤甚。许多误导的文章被当作经典转来转去,充斥着搜索引擎。网上信息越来越多,有用的信息有时候却越来越难找。本文参考网上一些较为权威的资料,但仍有可能理解不对,欢迎指正。

James Gosling 也玩点名游戏
Saturday, February 3rd, 2007

近期国内的Blogger玩点名游戏比较多,我个人是对这东西不感兴趣。

没想到James Gosling还这么有兴趣,回应别人的点名:

Five things you probably don’t know about me

MSDN 越来越垃圾了
Thursday, February 1st, 2007

这几天做ATL/COM,因为以前没有经验,所以就到处借鉴,最主要的当然是MSDN. 不过发现这东西越来越大,想找个资料却越来越难了。可能.NET的文档结构还是比较好的,而Win32 API,MFC/ATL这些就感觉有点被冷落了。

尤其是顺着IWebbrowser2, IHTMLDocument, IHTMLSelectionObject找下去的时候,找到createRange函数的说明,它的介绍却把返回对象的参考指向了Javascript的TextRange,真是让人晕。我千辛万苦才发现原来应该是IHTMLTxtRange.

很多时候打开一个URL微软会自作聪明地将你跳转到中文页面。我知道翻译过来的东西没几个好的,不过将就着看还可以。但这次我怎么也看不懂了,过了一会一抬头,发现赫然写着是机器翻译。

前些天偶然上CSDN,有人说微软正在将MSDN中文化(可能是几年前的留言),我们有福啦。有什么福,好多优秀书籍的中译本都糟蹋了原著了。更何况微软不可能把这么大一坨翻译过来。那个中文版的Visual Studio我就看不懂菜单,那天想格式化一块代码都不知道是哪个菜单项,幸好隐约记得Ctrl+K, Ctrl+F. 这还是微软的官方版本。

另外最近跟人交流的时候也都认为最好不要把自己束缚在微软的技术范围内。微软的技术就是要把人牢牢的绑在它旁边,你离开它就很难生存了。微软的东西上手容易,好像搭积木一样,老少皆宜,不过你要想深入理解、做出真正优秀、有水平的软件来就难多了。还是多关心那些发展良好的开源、平台无关技术为好,微软的小世界迟早要失败,或者以另一种形式生存。尽管现在的自由软件领域存在各种各样的问题,它总会走上更正确的道路。