C语言实现中文BGK、UNICODE、UTF8之间的编码转换

一、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编码0101010101001010-
utf编码1110_010110_01010110_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

©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页