字符串处理常用函数

截取字符串的一定的长度在解各种数据包中是非常常用的,怎么实现截取取决于数据包的数据类型。
1、截取两个字节长度:
定义为宏:

#define pntoh16(p)  ((uint16_t)                       \
                     ((uint16_t)*((const uint8_t *)(p)+0)<<8|  \
                      (uint16_t)*((const uint8_t *)(p)+1)<<0))

定义为函数:
uint16_t pntoh16(uint8_t* p)
{
return (uint16_t)|(uint16_t)(p+0)<<8|(uint16_t)(p+1)<<0);
}
2、截取三个字节长度:
定义为宏:

#define pntoh24(p)  ((uint32_t)*((const uint8_t *)(p)+0)<<16|  \
                     (uint32_t)*((const uint8_t *)(p)+1)<<8|   \
                     (uint32_t)*((const uint8_t *)(p)+2)<<0)

定义为函数:
uint32_t pntoh24(uint8_t* p)
{
return (uint32_t)((uint32_t)§<<16|(uint32_t)(p+1)<<8|(uint32_t)*(p+2)<<0);
}
3、截取四个字节长度:
定义为宏:

#define pntoh32(p)  ((uint32_t)*((const uint8_t *)(p)+0)<<24|  \
                     (uint32_t)*((const uint8_t *)(p)+1)<<16|  \
                     (uint32_t)*((const uint8_t *)(p)+2)<<8|   \
                     (uint32_t)*((const uint8_t *)(p)+3)<<0)

定义为函数:
uint32_t pntoh32(uint8_t* p)
{
return (uint32_t)((uint32_t)§<<24|(uint32_t)(p+1)<<16|(uint32_t)(p+2)<<8|(uint32_t)(p+3)<<0);
}
以下类似函数省略:

#define pntoh40(p)  ((uint64_t)*((const uint8_t *)(p)+0)<<32|  \
                     (uint64_t)*((const uint8_t *)(p)+1)<<24|  \
                     (uint64_t)*((const uint8_t *)(p)+2)<<16|  \
                     (uint64_t)*((const uint8_t *)(p)+3)<<8|   \
                     (uint64_t)*((const uint8_t *)(p)+4)<<0)

#define pntoh48(p)  ((uint64_t)*((const uint8_t *)(p)+0)<<40|  \
                     (uint64_t)*((const uint8_t *)(p)+1)<<32|  \
                     (uint64_t)*((const uint8_t *)(p)+2)<<24|  \
                     (uint64_t)*((const uint8_t *)(p)+3)<<16|  \
                     (uint64_t)*((const uint8_t *)(p)+4)<<8|   \
                     (uint64_t)*((const uint8_t *)(p)+5)<<0)

#define pntoh56(p)  ((uint64_t)*((const uint8_t *)(p)+0)<<48|  \
                     (uint64_t)*((const uint8_t *)(p)+1)<<40|  \
                     (uint64_t)*((const uint8_t *)(p)+2)<<32|  \
                     (uint64_t)*((const uint8_t *)(p)+3)<<24|  \
                     (uint64_t)*((const uint8_t *)(p)+4)<<16|  \
                     (uint64_t)*((const uint8_t *)(p)+5)<<8|   \
                     (uint64_t)*((const uint8_t *)(p)+6)<<0)

#define pntoh64(p)  ((uint64_t)*((const uint8_t *)(p)+0)<<56|  \
                     (uint64_t)*((const uint8_t *)(p)+1)<<48|  \
                     (uint64_t)*((const uint8_t *)(p)+2)<<40|  \
                     (uint64_t)*((const uint8_t *)(p)+3)<<32|  \
                     (uint64_t)*((const uint8_t *)(p)+4)<<24|  \
                     (uint64_t)*((const uint8_t *)(p)+5)<<16|  \
                     (uint64_t)*((const uint8_t *)(p)+6)<<8|   \
                     (uint64_t)*((const uint8_t *)(p)+7)<<0)

有时候我们数据是网络字节序(大端字节序:把值的低位存在高地址)的需要截取并转化成主机字节序(小端字节序:把值得低位存在地地址)。
附加一个判断本机大小端的简单程序
void litterorbig ()
{
unsigned int x = 0x12345678;
char c = (char)&x;
if (*c == 0x78) {
printf(“Little endian”);
} else {
printf(“Big endian”);
}
}
下面分别是截取两、三、四、五、六、七、八字节长度并转换的定义的宏,函数省略:

#define pletoh16(p) ((uint16_t)                       \
                     ((uint16_t)*((const uint8_t *)(p)+1)<<8|  \
                      (uint16_t)*((const uint8_t *)(p)+0)<<0))

#define pletoh24(p) ((uint32_t)*((const uint8_t *)(p)+2)<<16|  \
                     (uint32_t)*((const uint8_t *)(p)+1)<<8|   \
                     (uint32_t)*((const uint8_t *)(p)+0)<<0)

#define pletoh32(p) ((uint32_t)*((const uint8_t *)(p)+3)<<24|  \
                     (uint32_t)*((const uint8_t *)(p)+2)<<16|  \
                     (uint32_t)*((const uint8_t *)(p)+1)<<8|   \
                     (uint32_t)*((const uint8_t *)(p)+0)<<0)

#define pletoh40(p) ((uint64_t)*((const uint8_t *)(p)+4)<<32|  \
                     (uint64_t)*((const uint8_t *)(p)+3)<<24|  \
                     (uint64_t)*((const uint8_t *)(p)+2)<<16|  \
                     (uint64_t)*((const uint8_t *)(p)+1)<<8|   \
                     (uint64_t)*((const uint8_t *)(p)+0)<<0)

#define pletoh48(p) ((uint64_t)*((const uint8_t *)(p)+5)<<40|  \
                     (uint64_t)*((const uint8_t *)(p)+4)<<32|  \
                     (uint64_t)*((const uint8_t *)(p)+3)<<24|  \
                     (uint64_t)*((const uint8_t *)(p)+2)<<16|  \
                     (uint64_t)*((const uint8_t *)(p)+1)<<8|   \
                     (uint64_t)*((const uint8_t *)(p)+0)<<0)

#define pletoh56(p) ((uint64_t)*((const uint8_t *)(p)+6)<<48|  \
                     (uint64_t)*((const uint8_t *)(p)+5)<<40|  \
                     (uint64_t)*((const uint8_t *)(p)+4)<<32|  \
                     (uint64_t)*((const uint8_t *)(p)+3)<<24|  \
                     (uint64_t)*((const uint8_t *)(p)+2)<<16|  \
                     (uint64_t)*((const uint8_t *)(p)+1)<<8|   \
                     (uint64_t)*((const uint8_t *)(p)+0)<<0)

#define pletoh64(p) ((uint64_t)*((const uint8_t *)(p)+7)<<56|  \
                     (uint64_t)*((const uint8_t *)(p)+6)<<48|  \
                     (uint64_t)*((const uint8_t *)(p)+5)<<40|  \
                     (uint64_t)*((const uint8_t *)(p)+4)<<32|  \
                     (uint64_t)*((const uint8_t *)(p)+3)<<24|  \
                     (uint64_t)*((const uint8_t *)(p)+2)<<16|  \
                     (uint64_t)*((const uint8_t *)(p)+1)<<8|   \
                     (uint64_t)*((const uint8_t *)(p)+0)<<0)

将十六进制转化为字符串

int strtohex(unsigned char *str, int len, char *hexstr)
{
	int i, j;
	unsigned char c;

	static char binhex[16] = {
		   '0', '1', '2', '3', '4', '5', '6', '7',
		   '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
		   
	for(i = 0, j = 0; i < len; i++)
	{
		c = *str++;
        hexstr[j++] = binhex[c>>4];
        hexstr[j++] = binhex[c&0xf];
	}
	hexstr[j]= '\0';
	return j;
}

或者

void strtohex(unsigned char*str, int len, char*hexstr)
{
    int i;
    char cTemp;
    for (i = 0; i < len; i++)
    {
        cTemp = (str[i] & 0xf0) >> 4;
        hexstr[i * 2] = (cTemp > 9) ? cTemp + 0x41 - 10 : cTemp + 0x30;
        cTemp = str[i] & 0xf;
        hexstr[i * 2 + 1] = (cTemp > 9) ? cTemp + 0x41 - 10 : cTemp + 0x30;
    }
}

或者

void strtohex(unsigned char *str, int len, char *hexstr)
{
	int i, j = 0;
	for(i =0 ; i < len; i++)
	{
		sprintf(hexstr + 2 * i,"%02X", str[i]);
	}
	
}

将字符串转化为十六进制

void hextostr(char*hexstr, int len, unsigned char*str)
{
    int i;
    char cTemp;
    for (i = 0; i < len; i++)
    {
        cTemp = hexstr[i * 2];
        if (cTemp >= 'A' && cTemp <= 'F')
            cTemp = cTemp - 'A' + 10;
        else if (cTemp >= 'a' && cTemp <= 'f')
            cTemp = cTemp - 'a' + 10;
        else
            cTemp &= 0x0f;
            
        str[i] = cTemp << 4;
        cTemp = hexstr[i * 2 + 1];
        if (cTemp >= 'A' && cTemp <= 'F')
            cTemp = cTemp - 'A' + 10;
        else if (cTemp >= 'a' && cTemp <= 'f')
            cTemp = cTemp - 'a' + 10;
        else
            cTemp &= 0x0f;
        str[i] += cTemp;
    }
    return;
}

或者

int  hextostr(char *hexstr, int len, unsigned char *str)
{
	int i, j = 0;
	char temp[2] = {0};
	for(i =0 ; i < len; i+=2)
	{
		memcpy(temp, hexstr + i, 2);
		sscanf(temp, "%02X", &str[j]);
		j++;
	}
	return j;
}

注意以上函数没有检查数组的长度,需提前检查长度以防溢出。

如果要截取的长度超过4个字节,用memcpy()函数来实现比较好,如果数据包的是字符串形式的也可以用strcpy(),strncpy(),sprintf(),strlen()等函数实现,建议用memcpy函数
进行各种拷贝。


因最近需要处理IP地址,IP地址在数据包中的形式是32位数字,我们用long int 类型进行存储,点分十进制IP地址用字符串存储,下面是两种形式的转换函数。
长整型转换为字符串

void *long2str(long int num, char *str)
{
	sprintf(str, "%u.%u.%u.%u", num >>24 & 0xFF, num >>16 & 0xFF, num >>8 & 0xFF, num >>0 & 0xFF);
}

字符串转换为长整型

long int str2long(char *str)
{
	return ntohl(inet_addr(str));
}

说明:inet_addr()和ntohl ()函数需要包含<sys/socket.h>和<arpa/inet.h>系统头文件。
inet_addr()函数是将字符串转换为网络字节序返回值为无符号长整型,ntohl ()函数是将网络字节序转换为主机字 节序长整型。可用127.0.0.1:2130706433测试。

我们抓取smtp或者pop3等邮件协议数据包,然后打开经常看到乱码,那是因为数据经过了base64编码或者Quoted-printable 编码,下面是两种编解码的实现(仅针对字符集为utf-8比编码)。

base64 它是一种基于用64个可打印字符来表示二进制数据的表示方法。它通常用作存储、传输一些二进制数据编码方法!也是MIME(多用途互联网邮件扩展,主要用作电子邮件标准)中一种可打印字符表示二进制数据的常见编码方法!它其实只是定义用可打印字符传输内容一种方法,并不会产生新的字符集!
由于2的6次方等于64,所以可以用每6个位元为一个单元,对应某个可打印字符。我们知道三个字节有24个位元,就可以刚好对应于4个Base64单元,即3个字节需要用4个Base64的可打印字符来表示。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9 ,这样共有62个字符,此外两个可打印符号在不同的系统中一般有所不同。但是,我们经常所说的Base64另外2个字符是:“+/”
base64 编码

const char base64char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 

int base64_encode(const unsigned char *src_data, int src_len, char *dst_data, int dst_len)
{
	int i = 0, j = 0;
	int index = 0;
	
	for( ; i < src_len; i += 3) 
	{
		//第一个字节,直接取第一个前6位
		index = (int)((src_data[i] >> 2) & 0x3f);
		dst_data[j++] = base64char[index];
		//第二个字节,取第一个字节的后两位和第二个字节的前四位
		index = (int)((src_data[i] << 4) & 0x30);
		if(i + 1 < src_len)
		{
			index |= ((src_data[i + 1] >> 4) & 0x0f);
			dst_data[j++] = base64char[index];
		}
		else
		{
			dst_data[j++] = base64char[index];
			dst_data[j++] = '=';
			dst_data[j++] = '=';
		}
		//第三个字节,取第二个字节后四位和第三个字节前两位
		index = ((src_data[i + 1] << 2) & 0x3c);
		if(i + 2 < src_len)
		{
			index |= ((src_data[i + 2] >> 6) & 0x03);
			dst_data[j++] = base64char[index];
			//第四个字节直接取第三个字节后6六位
			index = src_data[i + 2] & 0x3f;
			dst_data[j++] = base64char[index];
		}
		else
		{
			dst_data[j++] = base64char[index];
			dst_data[j++] = '=';
		}	
	}
	dst_data[j] = '\0';
	return j;
}

base64 解码

int base64_decode(const char * base64, unsigned char * dedata)
{
    int i = 0, j=0;
    int trans[4] = {0,0,0,0};
    for ( ;base64[i] != '\0'; i += 4)
	{
        // 每四个一组,译码成三个字符
        trans[0] = num_strchr(base64char, base64[i]);
        trans[1] = num_strchr(base64char, base64[i+1]);
        // 1/3
        dedata[j++] = ((trans[0] << 2) & 0xfc) | ((trans[1]>>4) & 0x03);

        if (base64[i+2] == '=')
		{
            continue;
        }
        else
		{
            trans[2] = num_strchr(base64char, base64[i + 2]);
        }
        // 2/3
        dedata[j++] = ((trans[1] << 4) & 0xf0) | ((trans[2] >> 2) & 0x0f);

        if (base64[i + 3] == '=')
		{
            continue;
        }
        else
		{
            trans[3] = num_strchr(base64char, base64[i + 3]);
        }

        // 3/3
        dedata[j++] = ((trans[2] << 6) & 0xc0) | (trans[3] & 0x3f);
    }

    dedata[j] = '\0';

    return j;
}

Quoted-printable可译为“可打印字符引用编码”,编码常用在电子邮件中, 在邮件里面我们常需要用可打印的ASCII字符 (如字母、数字与"=")表示各种编码格式下的字符!Quoted-printable将任何8-bit字节值可编码为3个字符:一个等号"=“后跟随两个十六进制数字(0–9或A–F)表示该字节的数值。例如,ASCII码换页符(十进制值为12)可以表示为”=0C", 等号"="(十进制值为61)必须表示为"=3D",gb2312下“中”表示为=D6=D0。除了可打印ASCII字符与换行符以外,所有字符必须表示为这种格式。
Quoted-printable 编码

#define MAX_LINE_LEN  76 		//每行最大长度
int EncodeQuoted(const unsigned char* src_data, int src_len, char* dst_data, int dst_len)
{
	int nDstLen = 0;
	
	for(int i = 0; i < src_len; i++, src_data++)
	{
		if(dst_len < nDstLen + 3)
		{
			return nDstLen;
		}
		/*ASCII 33-60, 62-126原样输出,其余的需编码*/
		if((*src_data >= '!') && (*src_data <= '~') && (*src_data != '='))
		{
			*dst_data++ = (char)*src_data;
			nDstLen++;
		}
		else
		{
			sprintf(dst_data, "=%02X", *src_data);
			dst_data += 3;
			nDstLen += 3;
		}
		printf("***[%d][%s]***\n", nDstLen, dst_data);
		/*软换行*/
		if (nDstLen == MAX_LINE_LEN - 3)
		{
			sprintf(dst_data, "=/r/n");
			dst_data += 3;
			nDstLen += 3;
		}
	}
	/*结束符*/
	*dst_data = '\0';
	return nDstLen;
}

Quoted-printable 解码

//Quote-Printable 解码
int DecodeQuoted(const char* src_data, int src_len, char* dst_data)
{
	int dst_len = 0;
	int i = 0;

	while(i < src_len)
	{
		if(strncmp(src_data, "=/r/n", 3) == 0)
		{
			src_data += 3;
			i += 3;
		}
		else
		{
			if(*src_data == '=')
			{
				src_data++;
				char temp[2] = {0};
       			memcpy(temp, src_data, 2);
        		sscanf(temp, "%02X", &dst_data[0]);
				dst_data++;
				src_data += 2;
				i += 3;
			}
			else
			{
				*dst_data++ = *src_data++;
				i++;
			}
			dst_len++;
		}
	}
	*dst_data = '\0';
	return dst_len;
}

utf-8 和gbk字符集转换

将传入的gbk格式的编码转换成为utf-8形式的编码
int g2u(char *from_str, size_t fromlen, char *to_str, size_t tolen)
{
	int rv = convert("gb2312", "utf-8", from_str, fromlen, to_str, tolen);
	return rv;
}

//将传入的utf-8格式的编码转换成为gbk形式的编码
int u2g(char *from_str, size_t fromlen, char *to_str, size_t tolen)
{
	return convert("utf-8", "gb2312", from_str, fromlen, to_str, tolen);
	return 0;
}

//在两种类形的字符编码之间转换
int convert(const char *from_charset, const char *to_charset, char *from_str, size_t fromlen, char *to_str, size_t tolen)
{
	iconv_t cd;
	char **pin = &from_str;
	char **pout = &to_str;

	cd = iconv_open(to_charset, from_charset);
	if (cd == 0)
	{
		printf("iconv_open failed!\n");
		return -1;
	}
	memset(to_str, 0, tolen);
	if (iconv(cd, pin, &fromlen, pout, &tolen) == -1)
	{
		printf("from_str:%s\niconv failed!\n", from_str);
		iconv_close(cd);
		return -1;
	}
	iconv_close(cd);

	return 0;
}

本文章持续更新中~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值