- 文件名: 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、将字符转换为十六进制字符串
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码字符串转换为十六进制字符串
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;
}
}
}