C语言实现GB2312和UTF8之间的编码转换
GB2312
GB2312编码适用于汉字处理、汉字通信等系统之间的信息交换,基本集共收入汉字6763个和非汉字图形字符682个。GB2312中对所收汉字进行了“分区”处理,字符集分成94个区,每区含有94个汉字/符号,这种表示方式也称为区位码。
01-09区为特殊符号。
16-55区为一级汉字,按拼音排序。
56-87区为二级汉字,按部首/笔画排序。
10-15区及88-94区则未有编码。
举例来说,“啊”字是GB2312之中的第一个汉字,它的区位码就是1601。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”(也称“区字节)”,第二个字节称为“低位字节”(也称“位字节”)。
UTF-8
UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。UTF-8使用1~4字节为每个字符编码:
1)一个US-ASCIl字符只需1字节编码(Unicode范围由U+0000~U+007F)。
2)带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文等字母则需要2字节编码(Unicode范围由U+0080~U+07FF)。
3)其他语言的字符(包括中日韩文字、东南亚文字、中东文字等)包含了大部分常用字,使用3字节编码。
4)其他极少使用的语言字符使用4字节编码。
由此可知,对于中文,GB2312编码占用2个字节,UTF-8编码占用3个字节。
linux下编码工具
- 命令行工具
iconv -f encoding -t encoding inputfile
有如下选项可用:
输入/输出格式规范:
-f, --from-code=名称 原始文本编码
-t, --to-code=名称 输出编码
信息:
-l, --list 列举所有已知的字符集
输出控制:
-c 从输出中忽略无效的字符
-o, --output=FILE 输出文件
-s, --silent 关闭警告
--verbose 打印进度信息
示例:下面的命令是将一个utf8编码的文件转换为一个unicode编码的文件
iconv -f utf-8 -t gb2312 utf8file.txt -o gb2312file.txt
- C语言调用iconv库
下载libiconv库
根据个人开发板,交叉编译出动态库。
Ubuntu下好像可以直接包含头文件使用。
#include <iconv.h>
三个主要函数:
iconv_t iconv_open(const char *tocode, const char *fromcode);
此函数说明将要进行哪两种编码的转换,tocode是目标编码,fromcode是原编码,该函数返回一个转换句柄,供以下两个函数使用。
size_t iconv(iconv_t cd,char **inbuf,size_t *inbytesleft,char **outbuf,size_t *outbytesleft);
此函数从inbuf中读取字符,转换后输出到outbuf中,inbytesleft用以记录还未转换的字符数,outbytesleft用以记录输出缓冲的剩余空间。
注意:inbuf和outbuf都必须是有存储空间的不能定义为常量,如:char *inbuf = “abc” 或者是char *outbuf = "123"这样定义都是错误的。另外inbuf,inbytesleft,outbuf,outbytesleft这几个参数在使用过程中都会改变,最好是先保存一下原值,然后再使用。
int iconv_close(iconv_t cd);
此函数用于关闭转换句柄,释放资源。
代码示例:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "iconv.h"
//iInLen的长度不包括\0,应该用strlen。返回值是处理后的sOut长度
static int Utf8ToGb2312(char *sOut, int iMaxOutLen, const char *sIn, int iInLen)
{
char *pIn = (char *)sIn;
char *pOut = sOut;
size_t ret;
size_t iLeftLen=iMaxOutLen;
iconv_t cd;
cd = iconv_open("gb2312", "utf-8");
if (cd == (iconv_t) - 1)
{
return -1;
}
size_t iSrcLen=iInLen;
ret = iconv(cd, &pIn,&iSrcLen, &pOut,&iLeftLen);
if (ret == (size_t) - 1)
{
iconv_close(cd);
return -1;
}
iconv_close(cd);
return (iMaxOutLen - iLeftLen);
}
//iInLen的长度不包括\0,应该用strlen。返回值是处理后的sOut长度
static int Gb2312ToUtf8(char *sOut, int iMaxOutLen, const char *sIn, int iInLen)
{
char *pIn = (char *)sIn;
char *pOut = sOut;
size_t ret;
size_t iLeftLen=iMaxOutLen;
iconv_t cd;
cd = iconv_open("utf-8", "gb2312");
if (cd == (iconv_t) - 1)
{
return -1;
}
size_t iSrcLen=iInLen;
ret = iconv(cd, &pIn,&iSrcLen, &pOut,&iLeftLen);
if (ret == (size_t) - 1)
{
iconv_close(cd);
return -1;
}
iconv_close(cd);
return (iMaxOutLen - iLeftLen);
}
int main()
{
int nRtn = 0;
char* pszOri = "这是一个中文测试例程";
printf("byStr[%d]: %s\n", strlen(pszOri), pszOri);
char pszDst[50] = {0};
int iLen = Utf8ToGb2312(pszDst, 50, pszOri, strlen(pszOri)); // Utf8ToGb2312
printf("iLen: %d %s\n", iLen, pszDst);
printf("-----------\n");
char pszGbDst[50] = {0};
int iNewLen = Gb2312ToUtf8(pszGbDst, 50, pszDst, iLen); // Gb2312ToUtf8
printf("iNewLen: %d %s\n", iNewLen, pszGbDst);
return nRtn;
}
以上代码在Linux下编译,记得加上-liconv,代码先将UTF-8转换为GB2312,再从GB2312转为UTF-8,在SecureCRT下,按照GB2312编码方式显示,可以看到iLen后面的中文打印,按照UTF-8编码方式打印,可以看到byStr以及iNewLen后面的中文打印。
参考:Linux下 GB2312和UTF8转换接口