c函数strstr和sscanf组合高级技巧

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c函数strstr和sscanf组合高级技巧相关的知识,希望对你有一定的参考价值。

需求

上一章我们解析完了upnp协议中的http头部获取了xml文件的地址,使用http协议我们再次获取xml文件后需要解析xml,我们最害怕的就是引入一个又一个库,事实上,libupnp就引入了很多库,造成了程序臃肿,打印频繁,我们使用c语言的strstr和sscanf来手动解析xml,获取特定字符串。

1、分解url

upnp协议中的LOCATION是从http头部获取而来,要知道如何获取,请看这一片文章

解析http头部
接下来就是使用sscanf函数来获取http 的host 端口和路由信息,如
http://192.168.1.144:1551/description.xml
我们要分解成为 http://192.168.1.144:1551/ 和路由 /description,使用一个非常绚丽的技巧,如下所示:

	const char *pos = ss["LOCATION"].c_str();
		//const char *url = "http://192.168.1.144:1551/description.xml";
	char http[256] = {"http://" };
	char route[256] = { 0 };
	if (*(pos + 4) == ':')
	pos += 7;
	sscanf(pos, "%[^/]%s", &http[7],route);

以上先把http://填入缓冲中,然后让sscanf 中缓冲的指针指向第7个字节,开始往里放解析值,sscanf函数跳过http://,找到我们字符串的下一个"/"位置。 ok,我们使用http协议下载xml文件,下载结束,得到xml body

1、下载下来后xml body

我们使用http协议在程序中拿到了xml body体

<?xml version ="1.0"?>
<root xmlns = "urn:schemas-upnp-org:device-1-0" xmlns:dlna = "urn:schemas-dlna-org:device-1-0">
<specVersion>
<major>1</major >
<minor>0</minor >
</specVersion>
<device>
<UDN>uuid:bb5e-21ce-1111-11b2-f918-ec9c-3235-709a-</UDN>
<friendlyName>Living Room 1_5336_HiDMR</friendlyName>
<deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType >
<manufacturer>Hisilicon Technologies Co., Ltd</manufacturer>
<manufacturerURL>http://www.Hisilicon.com</manufacturerURL>
	<modelName>Hisilicon MediaRenderer</modelName>
	<modelNumber>1.1</modelNumber>
	<modelURL>http://www.Hisilicon.com</modelURL>
	<serialNumber></serialNumber>
	<dlna:X_DLNADOC xmlns:dlna="urn:schemas-dlna-org:device-1-0">DMR-1.50</dlna:X_DLNADOC>
	<dlna:X_DLNACAP xmlns:dlna="urn:schemas-dlna-org:device-1-0"></dlna:X_DLNACAP>
	<serviceList>
	<service>
	<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType >
	<serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>
	
	//需要获得下面这个controlURL
		<controlURL>/upnp/service/ConnectionManager/Control</controlURL>
	<eventSubURL>/upnp/service/ConnectionManager/Event</eventSubURL>
	<SCPDURL>/upnp/service/cms.xml</SCPDURL>
	</service>
	<service>
	<serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType >
	<serviceId>urn:upnp-org:serviceId:AVTransport</serviceId>
	
	//需要获得下面这个controlURL
	<controlURL>/upnp/service/AVTransport/Control</controlURL>
	<eventSubURL>/upnp/service/AVTransport/Event</eventSubURL>
	<SCPDURL>/upnp/service/avt.xml</SCPDURL>
	</service>
	<service>
	<serviceType>urn:schemas-upnp-org:service:RenderingControl:1</serviceType >
	<serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId>
	
	//需要获得下面这个controlURL
	<controlURL>/upnp/service/RenderingControl/Control</controlURL>
	<eventSubURL>/upnp/service/RenderingControl/Event</eventSubURL>
	<SCPDURL>/upnp/service/rcs.xml< / SCPDURL>
	</service>
	</serviceList>
	</device>
	</root>

这是一份upnp协议中的设备xml 描述,里面包含了几个关键信息,需要我们获取 我们需要的是:

1 设备名称
2 AVTransport 传输点的控制url
3 RenderingControl 点的控制url

熟悉upnp协议的同志们知道这个非常关键,下面我们来制定解决方案

2、定义数据结构

typedef struct s_xmlanalyse_c
{
	char friendlyName[64];
	char url_AVTransport[256];
	char url_RenderingControl[256];
	char url_ConnectionManager[256];
}s_xmlanalyse_c;

定义得到单个节点的内容函数,使用strstr函数来得到xml 字符串中的位置,将字符串内容使用memcpy来复制,字符串的本质是可打印并且最后一个字符是’\\0’,因此我们使用memcpy 和 '\\0’来生成新的字符串。


const char *GetValueOf_c(const char *sbody, const char *name, char *ret,int retlen)
{
	if (strlen(name) > 63)
		return NULL;
	char buf_fn1[64];
	char buf_fn2[64];
	sprintf(buf_fn1, "<%s>", name);
	sprintf(buf_fn2, "</%s>", name);
	const char *start = sbody;
	const char *end = sbody + strlen(sbody);

	char *fn =  &buf_fn1[0];
	char *fnn = &buf_fn2[0];
	int len = strlen(fn);
	const char * x1 = strstr(start, fn);
	const char * x2 = strstr(start + len,fnn);
	if (x1 != NULL && x2 != NULL)
	{
		x1 += len;
		int len2 = (int)(x2 - x1);
		if (len2 > retlen - 1)
			return NULL;
		memcpy(ret, x1, x2 - x1);
		ret[x2 - x1] = '\\0';
		return x2 + len+1;
	}
	return NULL;
}

获取三个节点内容

使用三个循环获取内容


int GetValueOf_Service_c(const char *fpos, s_xmlanalyse_c *value)
{
	const char * fname  = "serviceType";
	const char * fname2 = "controlURL";
	int flag = 0;
	char tmp[256];
	for (int i = 0; i < 3; i++)
	{
		fpos = GetValueOf_c(fpos, fname, tmp,256);
		if (fpos == NULL)
		{
			cout << "not good,xml is error" << endl;
			return -1;
		}
		if (strstr(tmp,"ConnectionManager") != NULL)
		{
			fpos = GetValueOf_c(fpos, fname2, value->url_ConnectionManager,sizeof(value->url_ConnectionManager));
			flag++;
		}
		else if (strstr(tmp,"AVTransport") != NULL)
		{
			fpos = GetValueOf_c(fpos, fname2, value->url_AVTransport,sizeof(value->url_AVTransport));
			flag++;
		}
		else if (strstr(tmp,"RenderingControl") != NULL)
		{
			fpos = GetValueOf_c(fpos, fname2, value->url_RenderingControl,sizeof(value->url_RenderingControl));
			flag++;
		}
	}
	if (flag == 3)
		return 0;
	return -1;
}

总结

我们使用分解分而治之对待软件工程的方式,首先
A 定义数据结构
B 得到流程 --》2.1 http 头部分解–》2.2 分解url --》2.3获取xml --》2.4获取节点内容
C 定义截取url函数
其中2.4 分解为如下步骤
D 定义获取节点内容的函数
E 定义 调用4 函数得到多个节点内容的函数

以上是关于c函数strstr和sscanf组合高级技巧的主要内容,如果未能解决你的问题,请参考以下文章

c函数sscanf的高级技巧

sscanf高级用法级正则表达式

c语言sscanf截取字符串函数获取后缀名

C基础函数的使用

C基础函数的使用

C语言sscanf()函数详解的代码