常用字符处理库


  • 文件名: mqlib.c
  • 作 者: Yuan Huafei【暮雨秋风】
  • 日 期: 2020-12-30
  • 描 述: 字符串及其它数据的特殊算法处理库
  • 修 订: V1.2.0
  • MuYuQiuFeng【暮雨秋风】
  • 1.2022-07-18: 新增十六进制字符串转10进制整数处理函数。
  • 2.2022-11-29: 新增字符转十六进制字符串处理函数。
  • 3.2022-12-01: 新增字符串分割处理函数。
  • 4.2022-12-14: 新增十六进制字符串转换为ascii字符串处理函数。
  • 5.2023-02-07: 优化字符串分割处理函数mq_strrep,支持空项分割。
  • 特别注意:
    所有操作内容需要进行初始化,字符串必定要附带结束符。
#include "mqlib.h"
#include "string.h"
一、特殊处理
1、十进制转BCD码
unsigned char mq_dtob(unsigned char dec)
{
   return (dec+(dec/10)*6);
}
2、BCD码转十进制
unsigned char mq_btod(unsigned char bcd)
{
   return (bcd-(bcd>>4)*6);
}
3、单字节负数转整型负数
  • 正数将保持原值
  • 针对char类型的负数无法直接转换为int型
  • 有时候也需要用uchar存储负数以节省空间
int mq_ctoi(char obj)
{
	int val = obj & 0x7F;
	
	if(obj > 127)
	{
		val = -((~obj&0x7F)+1);
	}
	return val;
}
4、由日期计算星期
  • 蔡勒公式 W=[C/4]-2C+y+[y/4]+[26(m+1)/10]+d-1
  • 返回 0-6 : 星期日、星期一 至 星期六
int mq_week(int year, int month, int day)
{
	int c, y, week;

	if (month == 1 || month == 2)
	{
		year--, month += 12;
	}
	c = year / 100;     // 计算世纪
	y = year - c * 100; // 获取年份尾数
	week = y + y / 4 + c / 4 - 2 * c;
	week += 26 * (month + 1) / 10 + day - 1;
	while (week < 0) week += 7;
	week %= 7;
	return week;
}
二、字符转换
1、将无符号整数转换为十六进制字符串
  • 可选择输出位宽以及添加前缀”0x”或后缀”H”或无前后缀
char *mq_itoha(char *s,unsigned int obj,unsigned char bw,char bf)
{
  int m = 0,n;
	unsigned char bs;
	char temp,*addr;
	
	if(bw > 8) return NULL;
	if(bf == '#') // 判断是否需要前缀
	{
		*s++ = '0';
		*s++ = 'x';
	}
	do{
		bs = obj % 16;
		s[m++] = bs <10 ? bs + '0': bs - 10 + 'A'; // 取出数字
	} 
	while((obj /= 16) > 0);  // 去除该位
	while(m < bw){ s[m++] = '0'; } // 以0补足位宽
	s[m]='\0';               // 添加结束符
	addr = &s[m];
	for(n = 0; n < --m; n++) // 将结果正向排列
	{
		temp = s[n];
		s[n] = s[m];
		s[m] = temp;
	}
	if(bf == 'H') // 判断是否需要后缀
	{
		*addr++ = 'H';
		*addr = '\0';
	}
	return addr;
}
2、将字符转换为十六进制字符串
  • 例:’0’->”30”,’A’->”41”
char *mq_atoha(char *s,char obj)
{
	int temp;
	
	*s++ = obj / 16 + '0';
	temp = obj % 16;
	*s++ = temp > 9 ? temp - 10 + 'A' : temp + '0';
	*s = '\0'; // 添加结束符
	return s;
}
3、将整数转换为ascii码格式
char *mq_itoa (char *s, int obj)
{
	int m = 0,n,sign = obj;
	
	char temp,*addr;
	obj = (obj < 0) ? -obj : obj;
	do{
		s[m++] = obj % 10 + '0';    // 取出数字
	} 
	while((obj /= 10) > 0);       // 去除该位
	if( sign < 0 ){ s[m++] = '-';}// 确定符号
	s[m]='\0';                    // 添加结束符
	addr = &s[m];
	for(n = 0; n < --m; n++)      // 将数字正向排列
	{
		temp = s[n];
		s[n] = s[m];
		s[m] = temp;
	}
	return addr;
}
4、将浮点数转换为ascii码格式
  • 可选择保留指定的小数位数
char* mq_ftoa(char* s, double obj, unsigned char dec)
{
	int obj1, bs = 1;
	char* addr = NULL;

	if (dec > 8 || dec < 1) return NULL;
	obj1 = obj;                // 取出整数部分
	s = mq_itoa(s, obj1);
	if (dec)
	{
		*s++ = '.';
		while (dec--) bs *= 10;
		// 取出小数部分并四舍五入扩大
		obj1 = (obj - (int)obj +0.5/bs) * bs;
		obj1 = obj1>0 ? obj1: -obj1; // 如果为负数小数部分不需要再次添加负号
		while (bs /= 10) // 小数的十分位为0
		{ 
			if (!(obj1 / bs)) *s++ = '0';
			else break;
		}
		if (!bs) { *s = '\0'; return s; } // 小数全部为
		addr = mq_itoa(s, obj1);
	}
	return addr;
}
5、十六进制字符串转整数
  • obj为目标字符串,bw为位宽(这里指字符串长度)
  • 目标字符串长度最大为 8
  • 例:两个字符时:”35”-> mq_hstrtoi(“35”,4) = 53 或者说 char ‘5’
  • 例:多个字符时:”357B”-> mq_hstrtoi(“357B”,4) = 13691
int mq_hstrtoi(const char *obj,unsigned char bw)
{
	int  val = 0,flag = 0;
	
	if(bw > 8) return 0;
	while(bw--)
	{
		val <<= 4;
		val += *obj >= '0' && *obj <= '9' ? *obj - '0'
		: *obj >= 'A' && *obj <= 'F' ? *obj - 'A'+10 
		: *obj >= 'a' && *obj <= 'f' ? *obj - 'a'+10 
		: (flag = 1);
		if( flag ) {val >>= 4; break;}
		obj++;
	}
	return val;
}
6、将十六进制字符串转换为ascii字符串
  • 十六进制字符串只包含0-9-A-F的字符
  • 开头允许出现非16进制,中途不得出现,出现视为结束,退出解析
  • 例:”QKP3041353334J”->”0A534”
char *mq_hstos(char *s,char *obj)
{
	int    connt = 0;
	char   *addr = obj;
	
	while(*addr != '\0')
	{
		if(*obj >= '0' && *obj <= '9')
		{
			addr++; connt++;
		}
		else if(*obj >= 'A' && *obj <= 'F')
		{
			addr++; connt++;
		}
		else if(*obj >= 'a' && *obj <= 'f')
		{
			addr++; connt++;
		}
		else // 非16进制字符
		{ 
			if(connt){break;} obj++; addr = obj;
		}
		
		if(connt && (connt % 2 == 0))
		{
			*s++ = mq_hstrtoi(obj,2);

			obj = addr;
		}
	}
	*s = '\0';
	return s;
}
7、将ascii码字符串转换为十六进制字符串
  • 例:”1234”->”3132333435”
char *mq_astohs(char *s,char *s1)
{
	while(*s1 != '\0')
	{
		s = mq_atoha(s, *s1++);
	}
	return  s;
}
三、字符连接
1、连接两个字符串
  • 返回连接后的字符串结束符的地址
  • 十六进制字符串只包含0-9-A-F的字符
  • 开头允许出现非16进制,中途不得出现,出现视为结束,退出解析
  • 例:”QKP3041353334J”->”0A534”
char *mq_strcat(char *s,char *s1)
{
	while(*s != '\0')s++;
	while(*s1 != '\0')
	{
		*s++ = *s1++;
	}
	*s = '\0';
	return  s;
}
2、连接两个字符串带间隔符
  • 可选择添加一个可选的间隔符,返回字符串结束符的地址
char *mq_astrcat(char *s,char *s1,char *sf)
{
	s =  mq_strcat(s,s1);
	return  mq_strcat(s, sf);
}
3、将整数转换为ascii码格式并连接到字符串末尾
  • 可选择添加一个可选的间隔符,返回字符串结束符的地址
char *mq_istrcat(char *s,int obj,char *sf)
{
	while(*s != '\0')s++;
	s = mq_itoa (s,obj);
	return  mq_strcat(s, sf);
}
4、将整数转换为十六进制ascii码格式并连接到字符串末尾
  • 可选择添加一个可选的间隔符,返回字符串结束符的地址
char *mq_hstrcat(char *s,int obj,unsigned char bw,char bf,char *sf)
{
	while(*s != '\0')s++;
	s = mq_itoha (s,obj,bw,bf);
	return  mq_strcat(s, sf);
}
5、将浮点数转换为ascii码格式并连接到字符串末尾
  • 可选择添加一个可选的间隔符,返回字符串结束符的地址
char *mq_fstrcat(char *s,double obj,unsigned char dec,char *sf)
{
	while(*s != '\0')s++;
	s = mq_ftoa (s,obj,dec);
	return  mq_strcat(s, sf);
}
四、字符分割截取
1、字符串复制
  • 复制一个到特定标志的字符串并返回源字符串标记出现的地址
char *mq_strcpyf(char *targ,char *src,char bf)
{
    if(*src==bf) {return src;} // 跳过
    while(*src != bf && *src != '\0')
    {
        *targ++ = *src++;
    }
	 *targ = '\0';
    return src;
}
2、字符串分割:字符方式
  • 以特定【字符】标记解析字符串
  • 输出一个分割的字符串列表,并返回列表的元素个数
  • 使用举例:
    void use_test(void)
    {
    	char test_str[] = "123,456,,,ABC,DEF";
    	char* lis[10];
    	unsigned short num = mq_strrep(test_str,',', lis);
    	unsigned short n;
    
    	printf("num = %d\n",num);
    	for (n = 0; n < num; n++)
    	{
    		printf("lis[%d]: %s\n",n,lis[n]);
    	}
    }
    Print Out:
    num = 6
    lis[0]: 123
    lis[1]: 456
    lis[2]: [NULL]
    lis[3]: [NULL]
    lis[4]: ABC
    lis[5]: DEF
unsigned short mq_strrep(char *s,char bf,char **lis)
{
	unsigned int num = 0;
	
	if(*s == NULL) return 0;
	lis[ num++ ] = s;
	while(*s)
	{
		if(*s == bf)
		{
			*s++ = '\0'; // 分隔符替换为空字符
			if(*s != '\0')
			{
				if(*s == bf) // 下一个字符也是分隔符,说明此项缺省
				{
					lis[ num++ ] = NULL;
					s--; // 缺省的项目也要计算在内
				}
				else
				{
					lis[ num++ ] = s;
				}
			}
		}
		s++;
	}
	return  num;
}
3、字符串分割:字符串方式
  • 以特定【字符】标记解析字符串
  • 输出一个分割的字符串列表,并返回列表的元素个数
  • 使用举例:
    void use_test(void)
    {
    	char test_str[] = "123456\r\nABCDEFG\r\n{df456}\r\nmqtt\r\nOK\r\n";
    	char* lis[10];
    	unsigned short num = mq_strreps(test_str,"\r\n", lis);
    	unsigned short n;
    
    	printf("num = %d\n",num);
    	for (n = 0; n < num; n++)
    	{
    		printf("lis[%d]: %s\n",n,lis[n]);
    	}
    }
    Print Out:
    num = 5
    lis[0]: 1233456
    lis[1]: ABCDEFG
    lis[2]: {df456}
    lis[3]: mqtt
    lis[4]: OK
unsigned short mq_strreps(char* s, char *bf, char** lis)
{
	unsigned short num = 0;
	unsigned short bf_len = strlen(bf);

	if (s == NULL) return  0;
	while (*s != '\0')    // 将一包数据分割
	{
		lis[num] = s;     // 数据的地址
		s = strstr(s, "\r\n");
		if (s != NULL)
		{
			num++;
			*s = '\0';
			s += bf_len; // 跳过 bf
		}
		else { break; }  // 不存在符合条件的数据
	}
	return  num;
}
4、字符截取
  • 截取关键词1(包括关键词1)到关键词2之间的字符,targ->目标地址
  • src->源地址,key1->关键词1, key2->关键词2,返回字符串的长度
unsigned short  mq_strcut(char *targ,char *src,char *key1,char *key2)
{
	char  *addr1,*addr2;
	unsigned short  len = 0;
	
	addr1 = strstr(src,key1);
	if(addr1 == NULL) return  0;
	addr2 = strstr(src,key2);
	if(addr2 == NULL) return  0;
	*addr2 = '\0';
	while(*addr1)
	{
		*targ++ = *addr1++;
		len++;
	}
	*addr2 = *key2;
	return  len;
}
五、数据空间处理
1、清空指定长度的空间
void mq_bzero(unsigned char *obj,unsigned int size)
{
	while(size)
	{
		*obj++ = 0;
		size--;
	}
}
2、数据大小端转换
  • bw:数据宽度(包括16,32,64bit等),bl:转换长度(数据个数)
  • 例: 2个空间连续的32(bw=32)位数,则转换长度为2个数据(bl=2)
void mq_swap(unsigned char *obj,unsigned char bw,unsigned int bl)
{
	unsigned int  m,n;
	unsigned char temp,w = bw/8;
	
	do
	{
		m = w;
		for(n = 0; n < --m; n++)
		{
			temp = obj[n];
			obj[n] = obj[m];
			obj[m] = temp;
		}
		obj += w;
	}
	while(--bl > 0);
}
3、选择排序算法
  • 从小到大排列
void mq_smin_sort(unsigned short *arr, int len)
{
    int m, n , min, temp;
	
    for(m = 0; m < len-1; m++)
    {
        min = m;
        for(n = m + 1; n < len; n++)
        {
            if(arr[min] > arr[n])
            {
                min = n;
            }
        }
        if(min != m)
        {
            temp = arr[m];
            arr[m] = arr[min];
            arr[min] = temp;
        }
    }
}

文章作者: Yuan Huafei
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Yuan Huafei !
  目录