一、GBK、UNICODE、UTF8之间编码的关系
GBK:GBK全名为汉字内码扩展规范,英文名Chinese Internal Code Specification。GBK 采用双字节表示,总体编码范围为8140-FEFE,首字节在81-FE 之间,尾字节在40-FE 之间,剔除 xx7F一条线。总计23940 个码位,共收入21886个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号883 个。
UNICODE:unicode是一种支持全世界所有语种字符的编码方式,每个字符统一采用两个字节表示,其中unicode中英文字符的编码和ascii编码一致。
UTF-8:UTF-8 是一种变长的编码方式,一般用 1~4 个字节序列来表示 Unicode 字符,也是目前应用最广泛的一种 Unicode 编码方式。UTF-8 编码算法:首字节码用来区分采用的编码字节数 ,如果首字节以 0 开头,表示单字节编码;如果首字节以 110 开头,表示双字节编码;如果首字节以 1110 开头,表示三字节编码,后续每个字节高两位始终是10,以此类推。
例如:
字符‘a’ | 第一字节 | 第二字节 | 第三字节 |
---|---|---|---|
unicode编码 | 01100001 | - | - |
utf编码 | 0_1100001 | - | - |
字符‘啊’ | 第一字节 | 第二字节 | 第三字节 |
---|---|---|---|
unicode编码 | 01010101 | 01001010 | - |
utf编码 | 1110_0101 | 10_010101 | 10_001010 |
总结:由上我们得知,utf-8是unicode编码的一种压缩方式,用于在文字传输时减少网络带宽的占用,这两种编码使用一定的算法即可相互转化;而GBK和UNICODE是两种不同的编码方式,他们字节并没有算法可以实现的规律,所以只能使用查表的方式来实现。
二、UNICODE、UTF8之间转化实现
UNICODE转UTF8:
由于在UNICODE编码中,ASCII编码高8位始终为0,所以使用一个字节即可表示,转化为UTF8编码不需要任何处理;而汉字的编码占用2个字节,转化为UTF8需要使用3个字节来表示,这里根据上述的编码方式来进行处理。
//参数:uni_in,要转码的Unicode编码地址,高字节在前
//参数:utf8_out,转码后输出的utf8编码存储地址的指针,在转码后地址自动向后移编
//返回值:下一个Unicode编码地址
u8 *uni2utf8 (const u8 *uni_in,u8 **utf8_out)
{
u16 c=0;
if ((uni_in)&&(c=(uni_in[0]<<8)|uni_in[1],c))
{
if (c<0x80)
{
**utf8_out=c;
(*utf8_out)++;
return (u8*)uni_in+2;
}
else
{
(*utf8_out)[0]=0xe0;
(*utf8_out)[0]|=uni_in[0]>>4;
(*utf8_out)[1]=0x80;
(*utf8_out)[1]|=((uni_in[0]<<2)|(uni_in[1]>>6))&0x3f;
(*utf8_out)[2]=0x80;
(*utf8_out)[2]|=(uni_in[1])&0x3f;
(*utf8_out)+=3;
return (u8*)uni_in+2;
}
}
return 0;
}
UTF8转UNICODE:
//参数:utf8_in,要转码的UTF8编码地址,高字节在前
//参数:uni_out,转码后输出的uni编码存储地址的指针,在转码后地址自动向后移编
//返回值:下一个utf8编码地址
u8 *utf82uni (const u8 *uft8_in,u8 **uni_out)
{
if ((uft8_in)&&(*uft8_in))
{
if (uft8_in[0]<0x80)
{
(*uni_out)[0]=0;
(*uni_out)[1]=*uft8_in;
(*uni_out)+=2;
return (u8*)uft8_in+1;
}
else
{
(*uni_out)[0]=uft8_in[0]<<4;
(*uni_out)[0]|=(uft8_in[1]>>2)&0x0f;
(*uni_out)[1]=(uft8_in[1]<<6);
(*uni_out)[1]|=(uft8_in[2])&0x3f;
(*uni_out)+=2;
return (u8*)uft8_in+3;
}
}
return 0;
}
三、UNICODE、GBK之间的转化
GBK转UNICODE:
GBK和UNICODE之间的转化需要使用到编码对照表,其中英文只需要把高字节设为0直接赋值即可。
static int g_unigbk_size=0;
//GBK编码转Unicode编码
//高字节在前
u8 *gbk2uni (u8 *gbk_in,u8 **uni_out)
{
u16 t[2];
u16 c;
u32 i, li, hi;
u16 n;
unsigned int cout;
u32 gbk2uni_offset=0;
if (gbk_in==0||*gbk_in==0) return 0;
if (*gbk_in < 0x80)
{
(*uni_out)[0]=0; //输出高字节在前
(*uni_out)[1]=*gbk_in;
(*uni_out)+=2;
return gbk_in+1;
}
else
{
c=(gbk_in[0]<<8)|gbk_in[1];
if(!g_unigbk_size)
{
// 打开编码对照表,得到编码对照表的大小
g_unigbk_size=unigbk_open();
}
gbk2uni_offset=g_unigbk_size/2;
if(g_unigbk_size)//存在
{
/* Unicode to OEMCP */
hi=g_unigbk_size/2;//对半开.
hi =hi / 4 - 1;
li = 0;
for (n = 16; n; n--)
{
i = li + (hi - li) / 2;
//比较编码是否相等,相等则输出对应的UNICODE编码
cout=unigbk_read(i*4+gbk2uni_offset,&t,4);
if (c == t[0]) break;
if (c > t[0])li = i;
else hi = i;
}
c = n ? t[1] : 0;
}else c=0;
(*uni_out)[0]=c>>8; //输出高字节在前
(*uni_out)[1]=c&0xff;
*uni_out+=2;
return gbk_in+2;
}
//return 0;
}
UNICODE转GBK:
//Unicode编码转GBK编码
//高字节在前
u8 *uni2gbk (u8 *uni_in,u8 **gbk_out)
{
u16 t[2];
u16 c;
u32 i, li, hi;
u16 n;
unsigned int cout;
u32 gbk2uni_offset=0;
if (uni_in==0||(c=(uni_in[0]<<8)|uni_in[1],c==0)) return 0;
if (c < 0x80)
{
(*gbk_out)[0]=c;
(*gbk_out)+=1;
return uni_in+2;
}
else
{
if(!g_unigbk_size)//如果没打开UNIGBK.BIN.
{
g_unigbk_size=unigbk_open();
}
gbk2uni_offset=0;
//因为在编码表文件中是低字节在前,这里重新指定c
//c=(uni_in[1]<<8)|uni_in[0];
if(g_unigbk_size)//存在
{
/* Unicode to OEMCP */
hi=g_unigbk_size/2;//对半开.
hi =hi / 4 - 1;
li = 0;
for (n = 16; n; n--)
{
i = li + (hi - li) / 2;
cout=unigbk_read(i*4+gbk2uni_offset,&t,4);
if (c == t[0]) break;
if (c > t[0])li = i;
else hi = i;
}
c = n ? t[1] : 0;
}else c=0;
(*gbk_out)[0]=c>>8; //输出高字节在前
(*gbk_out)[1]=c&0xff;
*gbk_out+=2;
return uni_in+2;
}
//return 0;
}
四、编码对照表
编码对照表只需要实现两个函数:
int unigbk_open(void);
此函数用于打开编码对照表,返回表的大小,单位字节;
int unigbk_read(int off,void *buff,int size);
此函数用于读取给定偏移的表数据,单位字节。
以下实现的是一个在单片机中可用的编码对照表,可以使用SD卡中或flash中的编码对照表:
/*
1,use uingbk file from sd card;
2,use uingbk file from flash manager;
3,unigbk table from mcu flash
*/
#define UNIGBK_TYPE 2
#if UNIGBK_TYPE==1
static FIL *UK_FILE=0;
static FILINFO *UK_FILINFO=0;
static char UK_FLAG=0; //标记是否打开了UNIGBK.BIN
static const char *UNIGBK_PATH="0:/SYS/UNIGBK.BIN"; //UNIGBK 存放路径
int unigbk_open(void)
{
if(!UK_FLAG)//如果没打开UNIGBK.BIN.
{
UK_FILE=malloc(sizeof(FIL));
UK_FILINFO=malloc (sizeof(UK_FILINFO));
if (f_stat((const TCHAR*)UNIGBK_PATH,UK_FILINFO)==FR_OK)
{
if(f_open(UK_FILE,(const TCHAR*)UNIGBK_PATH,FA_READ)!=FR_OK)
{
UK_FLAG=0;
free(UK_FILE);
free(UK_FILINFO);
}
else
{
UK_FLAG=1;
return UK_FILINFO->fsize;
}
}
else
{
UK_FLAG=0;
free(UK_FILE);
free(UK_FILINFO);
return 0;
}
}
if(UK_FILINFO)
return UK_FILINFO->fsize;
else
return 0;
}
int unigbk_read(int off,void *buff,int size)
{
UINT br=0;
if(UK_FILE)
{
f_lseek(UK_FILE,off);
f_read(UK_FILE,buff,size,&br);
}
return br;
}
#elif UNIGBK_TYPE==2
static u8 UK_FLAG=0;
static u32 g_uniTableOffset=0;
static u32 g_uniTableSize=0;
int unigbk_open(void)
{
if(!UK_FLAG)//如果没打开UNIGBK.BIN.
{
g_uniTableOffset=FLASH_FindFile("UNIGBK.BIN",&g_uniTableSize);
if(g_uniTableOffset)
{
UK_FLAG=1;
return g_uniTableSize;
}
else
{
UK_FLAG=0;
return 0;
}
}
if(g_uniTableOffset)
return g_uniTableSize;
else
return 0;
}
int unigbk_read(int off,void *buff,int size)
{
UINT br=0;
if(UK_FLAG)
{
br=FLASH_ReadData ((u8*)buff,g_uniTableOffset+off,size);
}
return br;
}
五、完整代码
链接:https://pan.baidu.com/s/1rnVFK0Bk–4ko5kSctTBdg
提取码:zngb