2009年十月月 发布的文章

使用iconv做内码转换

前不久某南方省份的客户反馈说我们的产品对某些生僻字(如“赟”)的转码支持的不好,终端收到后无法显示这个字。

经分析,发现类似“赟”这样的字在GB2312编码标准中并未收录,要想支持这样的生僻字的内码转换需要产品支持目前最新的中文编码标准GB18030。而我们的产品在诞生到现在就一直只支持GB2312,这就是导致这一问题的直接原因。

产品以前的代码库中内码转换的接口都是自己实现的,仅支持GB2312和UCS-2(即UNICODE16)之间的内码互转,如果要扩展就要更换码表。与其耗费力气找码表还不如挖掘一下开源世界最常用的内码转换工具iconv呢。iconv既提供了命令行转换工具(iconv),也提供一系列函数库接口供开发人员在代码里调用。很多知名的开源软件包(如vim等)都依赖iconv包。而iconv也几乎遍布所有unix和linux平台,iconv提供的转码支持也基本涵盖了世界范围内绝大多数主流字符集,其中支持的中文字符集就包括GBK, CP936, GB18030, BIG5等主流内码标准。

iconv的函数接口很简单,我迫不及待的想写一个例子测试一下了(不料,就在写下的这个简单的例子里我犯下了一个低级错误^_^)。

下面例子代码目的是将"赟"从UTF-8编码转换为GB18030编码(环境:GCC 3.4.6 on Solaris 10 for X86)。
#include

int main() {
        char    in[8];
        char    out[255];

        memset(in, 0, sizeof(in));
        memset(out, 0, sizeof(out));

        in[0]   = 0xe8; /* "赟"的UTF-8编码: E8B59F */
        in[1]   = 0xb5;
        in[2]   = 0x9f;

        size_t  inlen = strlen(in);
        size_t  outlen = sizeof(out);

        iconv_t cd;
        cd = iconv_open("gb18030","utf-8"); /* from utf-8->gb18030 */
        if (cd < 0) {
                printf("iconv_open failed!\n");
                return -1;
        }

        if (iconv(cd, &in, &inlen, &out, &outlen) < 0) {
                printf("iconv failed!\n");
                iconv_close(cd);
                return -1;
        }

    printf("out = %s\n", out);
    iconv_close(cd);
    return 0;
}
以上代码通过iconv_open获取一个转换描述符,这个描述符包含了转换信息(如从UTF-8转换到GB18030),然后调用iconv接口对传入的字符串进行转换,转换后的结果存储在OUT缓冲区中。

编译执行执行上面代码:
gcc -g testiconv.c -liconv
testiconv.c: In function `main':
testiconv.c:26: warning: passing arg 2 of `libiconv' from incompatible pointer type
testiconv.c:26: warning: passing arg 4 of `libiconv' from incompatible pointer type

./a.out
段错误 (core dumped)

为什么会dump core呢?回顾一下编译时的Warning信息,再对比一下iconv接口的原型:
size_t iconv (iconv_t cd, const char* * inbuf, size_t * inbytesleft,
              char* * outbuf, size_t * outbytesleft);
似乎没什么问题,但又仔细分析了一下Core的栈上信息,发现了一个低级失误:
问题就出在iconv的第二个和第四个参数上,我在栈上分配了数据in和out,并简单的将&in和&out作为参数传给了iconv。iconv要得是char **类型的参数。看起来&in和&out类型也是char **,但实则不然,这也是C语言的一个陷阱。以in为例,in本身就是栈上那个数组的首地址,&in的含义与in相同,同样是数组的首地址,所以&in = in,也就是说实际上传给iconv的是一个char*而不是char**,iconv在内部对一个char*执行*操作,并以为这是一个地址,显然会导致内存错误。

修改一下代码:
    char    *p_in    = in;
    char    *p_out    = out;
    if (iconv(cd, &p_in, &inlen, &p_out, &outlen) < 0) {
                printf("iconv failed!\n");
                iconv_close(cd);
                return -1;
    }
p_in变量在栈上分配,其本身的地址是&p_in,其值指向in这个数组的首地址,这样将&p_in传给iconv就万无一失了。
再编译执行,我们就得到了正确结果:
out = 赟

unix上有很多iconv实现,由于版本不同可能支持的字符集范围不同,所以为了保证代码行为一致,你可下载最新iconv包,并生成静态库(./configure –enable-static=yes),并让你的代码链接静态库。

午饭时从电视中得知:中国航天之父钱学森今天上午在北京离世。钱老可谓是中国科学家的楷模,对钱老的离世感到甚为惋惜。这里也道一句:“钱老,一路走好!”

说书单2009.10.28

十月以来,自已通过网购或换购还真收了不少书,这里说说:

国内关于伟大领袖毛主席的传记实在让我无法提起兴致,但哈佛大学教授Ross Terrill的《毛泽东传》我早在其出版时就关注过,它可以让我弄清楚毛主席在一个西方人眼中的事实形象。昨天偶然发现该书在卓越网的卖价居然比其他网店(诸如当当网)便宜近十元,这个“便宜”怎能不捡^_^,遂在昨天下了订单。今天再一看卓越的定价居然涨到了35.8元,涨了仅5元,不过还是比其他网店要便宜。

说到传记,就不能不提到最近卖的很火一本传记书-前Google中国区负责人李开复的新书 《世界因你不同 李开复自传》。以前读过李开复的《做最好的自己》,感觉很不错,所以这次也这本新书列入了购物车中。同时我也的确想通过李开复的书或多或少的去了解一下像微软、Google这样的大公司的一些运作“内幕"^_^。

今天在中国移动积分商场看到一套丛书套装《世界艺术瑰宝》很是动心,遂让同事先帮忙用2170积分换购下来(我的积分还差100多)。丛书共六册,均为全彩印刷,主要留作日后陶冶艺术情操、提升品位、家庭教育和旅游规划之用。

“灵修”二字我也是第一次遇到,十月初网购的一本名为《新世界 灵性的觉醒》就是此类书籍。书的作者在西方很有名,同时也是一个怪人。书还没开始看(还未做好心理准备^_^),初略翻阅了一下,觉得有些难度,或多或少的会折腾一下你的大脑神经的。

怪诞心理学》和《思维风暴》与“新世界”一书一起到手,前者是为了尝试了解一些大众心理学的内容,花了两个等晚点航班的时间段就翻阅完了;后者则纯粹为了让自己的大脑保持活跃的。

现在已是深秋近初冬季节,在暖气未供给之前,北方的屋内也都是冷冷的、湿湿的。捂在暖暖的被窝儿中看书那是何等的快哉。捧在我手中的这本《世界是平的》 自从07年从书刊批发市场买回后只看了一半,现在正以每天一章的速度阅读着,估计这周就能欣赏完这部三年前的名作了。如果再不快看,里面的一些观点可能就要过时了^_^。

榕城走一回

应客户之邀,本周一到福州做业务需求调研,周三返回沈阳。

以前从未去过榕城福州,领导下达调研任务时已是上周五。时间比较仓促,而且要求周一上午务必到达福州,因为客户方领导都较忙,也只有在周一才有机会见到客户领导。

安抚了LP后,周日下午我背上本子,带了几件随身衣物,就匆忙赶往机场。从沈阳出发到福州的航班都是有经停的,而且多是厦航、川航这样的小航空公司。为了能多陪LP一会儿,我选择了起飞较晚的航班,计划晚上22点到达福州。但是人算不如天算,航班晚点,周一凌晨1点才抵达福州长乐机场。在机场等飞机那是何等的煎熬,还好我随身带了一本《怪诞心理学》可以帮我打发时间。

此时的北方已进入深秋,夜晚温度近零度,但榕城却仍旧是一片温暖和煦,下飞机时机场地面温度依然有20多摄氏度。福州长乐机场距离市区较远,估计是我到过的城市里机场离市区最远的了。乘机场大巴用了将近一个多小时才到达终点(阿波罗大酒店)。打车到达闽江饭店Checkin时已是凌晨2点半了。走进房间后竟全无睡意,但想到白天还要到客户那开会,还是强迫自己入睡。

早上七点醒来,头有些痛,显然这短暂的睡眠还不能缓解我身体的疲劳。酒店提供早餐,早餐品种还算丰富,就是味道清淡了些,让我这个习惯了浓重口味的北方人有些不适^_^。和当地办事处的同事约好时间,上午做内部讨论,下午再去见客户。上午9点走出酒店,第一次清晰的看到榕城的闹市景象。和大多数省会城市一样,高楼大厦,繁忙喧嚣。瞥了一眼马路上的情况,看得出福州堵车也很严重^_^。既然福州号称榕城,那自然少不了榕树,我是不认识榕树的,但是猜也猜得出,大街两旁矗立的那些枝叶繁茂、树冠巨大的树肯定就是榕树了。

和北方相比,这里简直就是夏天,大街上男女老幼均是半袖裙子打扮,我也“入乡随俗”,脱去了厚重的外套,穿上了半袖衬衫。公司的办事处离酒店很近,走路也就5分钟,工作内容这里就不多说了。中午办事处领导在港式茶餐厅请客,这也算是入榕城后的第一顿饭了,遗憾的是少了些许本地菜的特色。席间听同事谈福州的房价,才知道福州房价要比沈阳高出一倍多,均价估计要上万,这在国内省会级城市里也算是排在前列的了。

下午见客户,途中路过闽江,江不宽,但却不失忙碌,闽江两旁码头林立,闽江中央船只往复。很想驻足欣赏,但无奈有公务在身^_^。

从客户那开完会出来已是华灯初上。同事带我去了一家当地特色的饭店吃了一顿牛排,这个牛排不是西餐中的那种烤牛肉,而是原生的牛排骨,一碗鲜美的汤中泡着两块包裹着厚实牛肉的牛排骨。以前从未这么吃过牛肉,这还是第一次,据同事介绍这家店里的牛肉是正宗的当地“达道牛肉”,肉质的确很嫩。

我有一个喜好,就是每到一地必到当地的博物馆,但是这次估计是真的没有时间了,因为周二有很多资料要准备,索性就在酒店里闷了一天。晚上出来随意到了一家小店吃了一口,然后在酒店附近转了转。福州的街道环境卫生一般,马路上街道旁可见随意丢弃的废物,很多国内城市(包括沈阳)也是这个样子。福州的电动自行车很多,起码比沈阳要多,每到饭点儿,在各家饭店门口你会看到一排排的电动车。福州的物价倒是不低,在超市里逛了逛发现无论是菜价还是水果价格都不在沈阳之下,甚至一些南方水果的价格也不低。

周三上午到客户现场与客户再次开会,就周一会上的一些问题和需求做应答。下午一点坐大巴赶往机场,三点的厦行航班居然又晚点了近一个小时。更可气的是经停南京时由于航空管制,竟坐在飞机上等了近一个多小时。回到家里已经是晚上10点多了。一进门,LP正坐在沙发上等我吃晚饭呢,那时那刻,心里美滋滋的。这两天发现自己的脸摸起来很顺滑儿,估计用福州的水的功劳,福州的水水质较软。

之前一直认为福州是个内陆临江城市,但是今天看了Google地图才发现原来福州机场东测就是大海。这次榕城行真是太匆忙了,甚至没有留下一张照片,下次有机会有时间一定细致“挖掘”一下福州。

有些像流水帐^_^。




这里是Tony Bai的个人Blog,欢迎访问、订阅和留言!订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您喜欢通过微信App浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:



本站Powered by Digital Ocean VPS。

选择Digital Ocean VPS主机,即可获得10美元现金充值,可免费使用两个月哟!

著名主机提供商Linode 10$优惠码:linode10,在这里注册即可免费获得。

阿里云推荐码:1WFZ0V立享9折!

View Tony Bai's profile on LinkedIn


文章

评论

  • 正在加载...

分类

标签

归档











更多