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组合高级技巧的主要内容,如果未能解决你的问题,请参考以下文章