使用 iconv 转换字符集

2013-06-17 (周一) by Weaver

最近在处理短信报文解包时,遇到了编码转换问题。部分短信内容使用UCS-2编码,我们需要将其转换为GB18030编码。

于是使用了著名的GNU项目libiconvlibiconv提供了命令行程序和相关的库函数。

iconv 命令行程序

Usage: iconv -l

显示支持的编码名称列表。

Usage: iconv [-c] [-s] -f FromCode -t ToCode [FileName...]

[-c] 当使用此选项时,不能被转换的字符将被默默丢弃;否则将导致一个转换错误。

[-s] 当使用此选项时,无效或不可转换的字符产生的错误信息将被忽略;但实际已转换的文本不受影响。

-f FromCode 指定输入的编码。

-t ToCode 指定输出的编码。

[FileName...] 待转换编码的文件列表。

使用举例:

iconv -c -s -f UTF-8 -t GBK test.xml

iconv 相关库函数

#include <iconv.h>

iconv_t iconv_open(const char *tocode, const char *fromcode);

size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);

int iconv_close(iconv_t cd);
  • iconv_open 函数初始化用于转换的内部缓冲区。成功则返回一个新分配的转换描述符;失败则置errno然后返回(iconv_t)(-1)。

  • iconv 函数进行实际的转换,iconv_t cd参数即是iconv_open函数的返回值;inbuf和outbuf是指向输入输出缓冲区指针的指针;inbytesleft和outbytesleft是输入长度和输出长度的剩余值,随着转换进行,逐渐减少。成功则返回已转换的字符数;失败则置errno然后返回(size_t)(-1)。

  • iconv_close 函数释放iconv_open函数打开的缓冲区。成功则返回(0);失败则置errno然后返回(-1)。

以下是这三个函数的简单封装,编译时需使用-liconv链接libiconv.a

#include <iconv.h>

#define  SUCCESS   0
#define  FAILURE  -1


int code_conv(const char *fromcode, const char *tocode, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft, char *errmsg)
{
    iconv_t cd = (iconv_t)(-1);
    size_t iconv_ret = (size_t)(-1);
    int close_ret = (-1);

    cd = iconv_open(tocode, fromcode);
    if (cd == (iconv_t)(-1))
    {
        sprintf(errmsg, "call iconv_open failed! errno = [%d], strerror = [%s]", errno, strerror(errno));
        return (FAILURE);
    }

    iconv_ret = iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft);
    if (iconv_ret == (size_t)(-1))
    {
        sprintf(errmsg, "call iconv failed! errno = [%d], strerror = [%s]", errno, strerror(errno));
        return (FAILURE);
    }

    close_ret = iconv_close(cd);
    if (close_ret == (-1))
    {
        sprintf(errmsg, "call iconv_close failed! errno = [%d], strerror = [%s]", errno, strerror(errno));
        return (FAILURE);
    }

    return (SUCCESS);
}

调用实例:

int main(int argc, char *argv[])
{
    int ret = 0;
    size_t inleft = 20;
    size_t outleft = 40;

    char instr[32];
    char outstr[64];
    char errmsg[512];

    memset(instr, 0x00, sizeof(instr));
    memset(outstr, 0x00, sizeof(outstr));
    memset(errmsg, 0x00, sizeof(errmsg));

    strcpy(errmsg, "零壹贰叁肆伍陆柒捌玖");

    char *p_instr = instr;
    char *p_outstr = outstr;

    ret = code_conv("GB18030", "UCS-2", &p_instr, &inleft, &p_outstr, &outleft, errmsg);
    if (ret)
    {
        printf("call code_conv failed! ret = [%d], errmsg = [%s]\n", ret, errmsg);
        return (-1);
    }

    printf("instr = [%s]\n", instr);
    printf("outstr = [%s]\n", outstr);
    printf("inleft = [%d]\n", inleft);
    printf("outleft = [%d]\n", outleft);

    return (0);
}

特别需要注意的是:两个指针 p_instr 和 p_outstr 。

假如不声明这两个指针,直接这样调用:

ret = code_conv("GBK", "UTF-8", &instr, &inleft, &outstr, &outleft, errmsg);

即直接取instr和outstr的地址传入code_conv函数,则可能会产生core dump。

以instr为例,instr本身是instr[]数组的首地址,&instr的含义与instr相同,同样是数组的首地址,所以&instr = instr,这样传给code_conv函数的就是一个char *,而不是char **


Python内置函数实现进制转换

2013-06-05 (周三) by Weaver

使用Python内置函数:bin()、oct()、int()、hex()可实现进制转换。

read more