UTF-8,Unicode,GB2312编码转换(C语言)

UTF-8,Unicode,GB2312编码转换(C语言)

最近在做MQTT客户端的时候,遇到需要将输入的GB2312中文转换为UTF8的问题,这边做一个整理。

UTF-8转GB2312

代码:

/*!
 * \brief   UTF8至Gb2312 转换函数
 *
 * \details UTF8 -> Unicode -> Gb2312
 *   
 * \param   const char* gb  输入UTF8字符串
 *      int len     输入长度
 *     char *temp    输出Gb2312字符串
 *
 */
void Utf8ToGb2312(const char* utf8, int len, char *temp)
{
	int byteCount = 0;
	int i = 0;
	int j = 0;
 
 	while (i < len)
 	{   
		switch(GetUtf8ByteNumForWord((char)utf8[i]))
		{
		case 0:
			temp[j] = utf8[i];
			byteCount = 1;
			break;
		  
  		case 2:
			temp[j] = utf8[i];
			temp[j + 1] = utf8[i + 1];
			byteCount = 2;
			break;
		case 3:
			//这里就开始进行UTF8->Unicode
			temp[j] = ((utf8[i] & 0x0F) << 4) | ((utf8[i + 1] >> 2) & 0x0F);
			temp[j+1] = ((utf8[i + 1] & 0x03) << 6) + (utf8[i + 2] & 0x3F);
   			//取得Unicode的值
			memcpy(&unicodeKey, (temp + j), 2);
			//根据这个值查表取得对应的GB2312的值
			gbKey = getgb(unicodeKey); 
			/* 因为gbKey是unsigned short类型,STM32是小端存储,保存成char类型时需要转一下 */
			if (gbKey != 0)
			{
				gbKey = (gbKey >> 8) | (gbKey << 8);
				memcpy((temp + j), &gbKey, 2);
			}
			byteCount = 3;
			break;
		case 4:
			byteCount = 4;
			break;
		case 5:
			byteCount = 5;
			break;
 		case 6:
	  		byteCount = 6;
   			break;
		default:
			break;    
		}
		i += byteCount;
		if (byteCount == 1)
		{
			j++;
		}
		else
		{
			j += 2;
		}
	}
}

/*!
 * \brief   计算当前字符包含字节数
 *
 * \details 通过第一个字节的高位1的数量计算
 *   
 * \param   char firstCh  第一个字节
 *
 * \retval  字节数
 */
int GetUtf8ByteNumForWord( char firstCh )
{
	char temp = 0x80;
	int num = 0;
	
	while (temp & firstCh)
	{
	 num++;
	 temp = (temp >> 1);
	}
	return num;
}

const unsigned short giGBCount=21327;

/*!
 * \brief   Unicode -> Gb2312
 *
 * \details 通过查表将Unicode转为Gb2312
 *   
 * \param   unicode  输入的Unicode
 *
 * \retval  转换得到的Gb2312
 */
static unsigned short getgb(unsigned short int unicode)
{
	int i;
  
	for(i=0; i<giGBCount; i++)
	{
		if (giGB2312[i][1] == ntohs(unicode)) 
		{
			return ntohs(giGB2312[i][0]);
		}
	}
	return 0xFFFF;
}

引来一段UTF-8与Unicode的转换规则:

  U-00000000 - U-0000007F: 0xxxxxxx  

  U-00000080 - U-000007FF: 110xxxxx 10xxxxxx  

  U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx  

  U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  

  U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  

  U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 		

第一个字节高位有几个1就代表此字符由几个字节组成,可以以此解析一长串UTF-8,如GetUtf8ByteNumForWord中的实现。比如当拿到一串字节流时,第一个字节是11100110,高位有3个1,那么再往后取2个字节,总共3个字节,假设得到:11100110 10110001 10001001 根据3字节结构即:1110-0110 10-110001 10-001001,提取出来即0110 1100 0100 1001,最后得到Unicode码:6C49,是“汉”字。函数getgb是一个简单的查表函数,giGB2312是转换码表,手头没有的朋友可以在GB2312,Unicode码表下载 。函数ntohs以及后面的htons都是大小端转换函数,如果机器是小端则需要转换,如果是大端则不需要。

GB2312转UTF-8

代码:

/*!
 * \brief   Gb2312至UTF8 转换函数
 *
 * \details Gb2312 -> Unicode -> UTF8
 *   
 * \param   const char* gb  输入Gb2312字符串
 *      int len     输入长度
 *     char *temp    输出UTF8字符串
 *
 * \retval  转换得到的UTF8长度
 */
int Gb2312ToUtf8(const char* gb, int len, char *temp)
{
	int i=0,j=0;
	unsigned short iTmp;
	
	while(i<len)
	{
		if ((unsigned char)gb[i] <= 0xa0)
		{
		  iTmp=gb[i];
		  iTmp=htons(iTmp);
		  i++;
		}
		else
		{
		     iTmp=getun(*(unsigned short int*)(gb+i));
			i+=2;
		}
		/* 从getun返回的时候已经被转成大端了,下面再转成小端*/ 
		iTmp = iTmp >> 8 | iTmp << 8;//这里本没有转的必要,getun由于系统是小端将数据转了一下,这里转回来
		if(iTmp <= 0x7F)
		{
			temp[j] = iTmp;
			j++;
		}
		else if(iTmp <= 0x7FF)
		{
			temp[j] = (iTmp >> 6) | 0xC0;
			temp[j+1] = (iTmp & 0x3F) | 0x80;
			j += 2;
		}
		else if(iTmp <= 0xFFFF)
		{
			temp[j] = (iTmp >> 12) | 0xE0;
			temp[j+1] = ((iTmp >> 6) & 0x3F) | 0x80;
			temp[j+2] = (iTmp & 0x3F) | 0x80;
			j += 3;
		}
		else
		{
			/*暂不考虑支持*/
		}
	}
	
	return j;
}

/*!
 * \brief   Gb2312 -> Unicode 
 *
 * \details 通过查表将Gb2312转为Unicode
 *   
 * \param   unicode  输入的Gb2312
 *
 * \retval  转换得到的Unicode
 */
static unsigned short getun(unsigned short int gb)
{
	int i;
	
	for(i=0;i<giGBCount;i++)
	{
		if (giGB2312[i][0]==gb)
		{
			return htons(giGB2312[i][1]);
		}
	}

	return 0xFFFF;
}

结语

代码中涉及到一些大小端的概念,如果想从事网络编程相关的工作必须了解。
参考资料:
UTF-8, Unicode, GB2312格式串转换之C语言版
你的机器是大端还是小端?

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值