RapidXml读取并修改XML文件

RapidXml读取并修改XML文件

RapidXml介绍

     RapidXml尝试创建最快的XML解析器,同时保留可用性,可移植性和合理的W3C兼容性。它是一个用现代C ++编写的原位解析器,解析速度接近strlen在同一数据上执行的函数。它仅仅只由四个头文件组成,并不要单独进行配置编译,使用起来非常方便。
在RapidXml的官网上,它发布了与其他xml解析库的速度对比,应该可以说RapidXml是目前最快的xml解析器。
在这里插入图片描述
RapidXml通过使用以下几种技术实现了它的速度:
1,原位解析。构建DOM树时,RapidXml不会复制字符串数据,例如节点名称和值。相反,它存储指向源文本内部的指针。
2,使用模板元编程技术。这允许它将大部分工作转移到编译时。通过模板的魔力,C ++编译器为您使用的任何解析器标志组合生成解析代码的单独副本。在每个副本中,所有可能的决定都在编译时进行,并且省略所有未使用的代码。
3,广泛使用查找表进行解析。
4,手动调优的C ++,在几个最流行的CPU上进行了分析。

RapidXml读取与修改xml文件

     我们以下面这个xml为例,找到key值为"ReportIndex"的节点,并在这个节点前面插入一个新的节点"",同时将key值为"ReportIndex"的节点以及后面的节点的offset值都加上4。

<hsbinmsg limit_size="5096" note="hsbinmsg msgtype info">
	<head note="packet head">
		<field key="msgtype" type="6" offset="0"/>
		<field key="bodylength" type="6" offset="4"/>
	</head>
	<tail note="packet tail">
		<field key="checksum" type="6" offset="-1"/>
	</tail>
	<msgtype id="202098" name="DesignationReport(202098)">
		<dataset note="">
			<field key="ReportIndex" type="7" offset="12" size="8"/>
			<field key="app1id" type="17" offset="20" size="3"/>
		</dataset>
	</msgtype>
</hsbinmsg>

     CXmlParse解析类的头文件如下:

#pragma once
#include "rapidxml.hpp"
#include "rapidxml_utils.hpp"
#include "rapidxml_print.hpp"
#include <string>

#define XML_NODE_NAME  "msgtype"
#define XML_NODE_NAME2 "ReportIndex"

#define OFFSET_SIZE		4

class CXmlParse
{
public:
	CXmlParse(const std::string &strPath);
	~CXmlParse();

private:
	int LoadXml();

	//找到key为name的属性
	bool GetAttrByName(const char* name, rapidxml::xml_node<>* pNode, rapidxml::xml_attribute<> *&pAttr);

	void UpdateAtributeValue(rapidxml::xml_document<> &doc, rapidxml::xml_attribute<>* &pAttr, int nOffset);

	void MakeFileCopy(const std::string& strPath);

private:
	std::string m_strPath;
	std::string m_strNewPath;
};

     cpp实现文件:

#include "XmlParse.h"
#include <windows.h>
#include <iostream>


CXmlParse::CXmlParse(const std::string &strPath)
{
	m_strPath = strPath;
	MakeFileCopy(m_strPath);
	if (LoadXml())
	{
		std::cout << "XML处理成功" << std::endl;
	}
	else
	{
		std::cout << "XML处理失败" << std::endl;
	}
}

CXmlParse::~CXmlParse()
{

}

//加载xml
int CXmlParse::LoadXml()
{
	if (m_strPath.empty())
	{
		return -1;
	}

	try
	{
		rapidxml::file<> file(m_strPath.c_str());
		rapidxml::xml_document<> doc;
		doc.parse<0>(file.data());

		//根节点
		rapidxml::xml_node<>* root = doc.first_node();
		rapidxml::xml_node<>* pNode = root->first_node();

		while (pNode)	//第二层节点head,msgtype
		{
			std::string strName = pNode->name();
			if (strName.compare(XML_NODE_NAME) == 0)   //找到msgtype
			{
				rapidxml::xml_node<>* pNode2 = pNode->first_node();	//第三层节点dataset
				if (pNode2)
				{
					rapidxml::xml_node<>* pNode3 = pNode2->first_node();	//第四层节点field,只找第一个
					bool bFind = false;
					rapidxml::xml_attribute<> *attr;
					if (GetAttrByName("key", pNode3, attr))
					{
						if (strcmp(attr->value(), "ReportIndex") == 0)
						{
							bFind = true;
						}
					}

					if (bFind)
					{
						//遍历第四层节点field节点, offset偏移量+4
						while (pNode3)
						{
							rapidxml::xml_attribute<> *attrSize;
							GetAttrByName("offset", pNode3, attrSize);
							UpdateAtributeValue(doc, attrSize, OFFSET_SIZE);
							pNode3 = pNode3->next_sibling();
						}

						//只要找到了一个key="ReportIndex"的field,那么就需要新插入一个node
						rapidxml::xml_node<> *pNewNode = doc.allocate_node(rapidxml::node_element, "field");
						if (pNewNode)
						{
							pNewNode->append_attribute(doc.allocate_attribute("key", "PartitionNo"));
							pNewNode->append_attribute(doc.allocate_attribute("type", "6"));
							pNewNode->append_attribute(doc.allocate_attribute("offset", "8"));
							pNewNode->append_attribute(doc.allocate_attribute("size", "4"));
							pNode2->prepend_node(pNewNode);
						}
					}
				}
			}

			pNode = pNode->next_sibling();
		}

		std::ofstream out(m_strNewPath);
		out << doc;
		out.close();	

		return 1;
	}
	catch (...)
	{
		std::cout << "XML解析出现异常" << std::endl;
		return -1;
	}
}

//获取到节点中key为name的属性
bool CXmlParse::GetAttrByName(const char* name, rapidxml::xml_node<>* pNode, rapidxml::xml_attribute<> *&pAttr)
{
	if (pNode == NULL)
		return false;

	for (rapidxml::xml_attribute<> *attr = pNode->first_attribute(); attr; attr = attr->next_attribute())
	{
		if (strcmp(attr->name(), name) == 0)
		{
			pAttr = attr;
			return true;
		}
	}

	return true;
}

//更新属性的值
void CXmlParse::UpdateAtributeValue(rapidxml::xml_document<> &doc, rapidxml::xml_attribute<>* &pAttr, int nOffset)
{
	if (NULL == pAttr)
		return;
	const char* oldValue = pAttr->value();
	int nValue = atoi(oldValue);
	nValue += nOffset;
	char str[12];
	memset(str, 0, 12);
	_itoa_s(nValue, str, 10);
	pAttr->value(doc.allocate_string(str), strlen(str));
}

//根据原路径得到新路径(原路径名+“2”)
void CXmlParse::MakeFileCopy(const std::string& strPath)
{
	std::string strNewPath = strPath;
	int nPos = strNewPath.find_last_of(".");
	if (nPos != std::string::npos)
	{
		std::string strTemp = strNewPath.substr(nPos + 1);
		strNewPath = strNewPath.substr(0, nPos);
		strNewPath += "2.";
		strNewPath += strTemp;
		m_strNewPath = strNewPath;
	}
}

     RapidXml遍历节点跟TinyXml遍历节点的方式一样,都是先获取根节点,然后获取到根节点下方的first_node(),然后通过next_sibling()不停地指向下一个兄弟节点,同时兄弟节点也可以深度去遍历。获取节点的各个属性值也是一样的使用方式。
     本程序为了和原xml文件作对比,就创建了新的文件,如果想在原文件上做修改,直接将std::ofstream out(m_strNewPath);中的m_strNewPath替换为m_strPath即可。
类封装好后,调用也非常方便。

CXmlParse parse(strPath);

     附上完整例子代码:Demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Simple Simple

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值