标签 Linux 下的文章

使用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),并让你的代码链接静态库。

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

为Ubuntu下的Firefox提速

部门的一套基础库刚刚移植到Linux上,为了测试该库,我将工作环境切换到了Ubuntu Linux下面。切换后居然发现Ubuntu下的Firefox访问网页巨慢无比,Firefox显示时明时暗,总是被挂起。同样的公司网络环境在Windows下使用Firefox访问互联网很顺畅,没有卡的现象。看来是时候给Ubuntu下的firefox提提速了。

Google了一下才发现反映类似现象的人为数不少啊,在Ubuntu中文论坛中得到了一些答案。有人建议关闭ipv6;还有人则建议install dnsmasq。不是很明白其中的理由,照做就是了。

首先关闭IPv6。打开一个终端,在终端下输入:"gksudo gedit /etc/modprobe.d/aliases";在文件中搜索到"alias net-pf-10 ipv6",注释掉其所在行,保存退出。再在终端下输入:"gksudo gedit /etc/modprobe.d/blacklist",在其中加上一行"blacklist ipv6 ",保存退出。最后重启系统使之生效。验证IPv6是否被关闭的方法:打开一个终端输入:"ip a | grep inet6",如果没有任何输出就说明 IPv6确实被关闭了。

其次,安装dnsmasq。在终端命令行下执行“sudo apt-get install dnsmasq”,安装完毕后,执行sudo gedit /etc/dnsmasq.conf,将“#resolv-file=”一行替换为"resolv-file=/etc/resolv.dnsmasq.conf"。然后执行“sudo cp /etc/resolv.conf /etc/resolv.dnsmasq.conf”,再编辑/etc/resolv.conf文件,保证在该文件中只保留"nameserver 127.0.0.1"一行即可,然后重启系统使dnsmasq生效。

果不其然,重启后的firefox恢复了和在Windows上一样的迅捷,不过遗憾的是由于修改了两处,不知道到底是上面哪种方法真正有效果的^_^。BTW,我的Ubuntu是7.10的,其自带的Firefox还是2.0.0.6版的,目前Firefox for Linux最新版已经是3.0.4了,这次顺便将firefox升级。使用apt-get居然没有3.0.4版的源,无法在线安装。更新源挺耗时的,还是直接到mozilla网站上下载吧。下载后的firefox是一个tar.bz2的包,这个如何安装呢?以前都是apt-get install的,还没有这么安装过,还好有Google。Ubuntu默认的firefox-2.0版安装在/usr/lib/firefox下,在/usr/bin下有firefox的一个符号链接。你通过命令行执行firefox或者点击桌面firefox图标启动firefox时实际上执行的都是/usr/lib/firefox/下的可执行文件,这样我就将下载的3.0.4的安装包通过tar -jxvf解压到本地目录,将/usr/lib/firefox备份,将解压后的3.0.4版本目录移到/usr/lib下,目录名仍然称作firefox,这样就可以顺滑过渡到3.0版了。现在你再启动firefox,查看“About”,就会看到版本已经升级到3.0.4了^_^。

上午发现同事的办公桌上摆着一款明晃晃的iPhone。这是我第一次如此近距离接触iPhone,第一印象就是"简洁",机身正面只有一个圆按钮,其余都是屏幕,黑色的机身透露着高贵。拿起来,挺沉,后盖应该是金属的,做工很精致。按下圆按钮,屏幕亮起,很清晰。屏幕上显示的菜单看起来与普通诺基亚手机的菜单分布没有太大区别。想滑动窗口看看还有多少菜单项,居然找不到箭头,经iPhone主人提示:用手指轻轻在屏幕上一划,屏幕就滚动到下一屏,太帅了。同事说iPhone就是游戏机,里面的实况足球游戏很好玩,我也打开游戏尝试了一吧。游戏在iPhone的屏幕左下角模拟显示了一个十字方向键,在右下角则模拟有A, B键。刚开始玩时还不适应,因为始终感觉手指上没有反馈的感觉,毕竟手指直接接触平直的屏幕完成控制挺难的,控制好也许更难。游戏特别流畅,难怪Android平台的founder Andy Rubin说iPhone与5年前一台PC的配置不相上下。iPhone在bestbuy网站最低卖价199美刀。不过需要和AT&T签署协议,核算下来与在国内买一个水货的成本不相上下了。Android平台目前虽未成熟,但发展势头也很快,不久的将来Android和iPhone之间势必有一场激烈的竞争。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

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

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

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats