背景
????????在我们平时的开发中通常会将一些用户信息或是一些配置信息写入到文件中保存起来,但是使用最简单的 .txt 文件文件进行存储显得不够高级。准确的说,打开任意程序安装目录,虽然能看到文件文件的存在,但基本都是 ReadMe.txt,基本看不到有把这类信息保存在文本文件中的情况。 ????????进而我们寻求一种更加常见的信息存储方式,使用 xml 文件。至于 xml 文件的格式和基本知识,如果有不熟悉的同学请 -------> 戳这
1. xml 文件结构简介
????????xml 文件的格式一般是树形结构,如下图: ????????如上图,绿色部分是 xml 的文件头信息,这里的问号是一种特殊格式,属于正常的 xml 文件内容。 ????????绿色这一样的内容可以这样解释:这个标签的标签名为 xml ,它的版本 version 为 1.0,文件编码 encoding 为 UTF-8。在这里,xml 标识标签值,而有等号连接的是属性,等号左侧为属性名,右侧为属性值。如这个标签有两个属性,它们的属性名分别是 version 和 encoding,属性值分别为 1.0 和 UTF-8。那么下面的标签也是这样解释。 ????????标签语言有起始标签和结束标签。即<></>。在起始标签和结束标签之间的内容属于本标签的内容。若起始标签和结束标签之间还有别的起始标签和结束标签,那么里面的标签就是当前标签的子级,而当前标签就是内部标签的父级,这样标签之间就有了父子级关联关系,就如同数据结构中的树一般。 ????????如上图的 excel 标签,他的起始标签和结束标签之间还有两个 table 标签。而 table 标签的起始标签和结束标签之间又有三个 colum 标签。也就是说,excel 标签有两个 table 标签作为子级标签,而每个 table 标签又拥有 colum 标签作为它的子级标签。 ????????就这样,我们可以根据父子级关系找到任意标签所在位置,亦可以根据位置查找到指定的标签。 ????????值得一提的是,一个 xml 文件中除去头文件标签后还会剩下一个一个标签作为所有后续加入的标签的祖先节点。如上图中的 excel 标签。xml 文件的读写操作也是建立在这一特性之上的。
2. xml 文件读写
2.1 前期准备
编程环境:
Visual Studio 2015 ; Windows 10 ;
第三方库源文件下载:
https:
????????如上图,解压后我们只需要这几个文件就可以了,把它们加入到我们项目中。
????????如果您知道如何将这些文件添加至项目中,可以跳过下面的内容,直接看 2.2。
????????新建 vs 控制台应用程序,并将文件拷贝至 stdafx.h 文件所在目录下(这里是防止后面引入后编译过程中引发的 是否忘记添加 #include “stdafx.h”? 的编译错误。对新手并不友好,干脆先简单一点,能用再说。)。如下图所示: ????????然后将这些文件添加到 vs 项目当中,如下图所示: ????????如果不会创建文件夹的话,可以右击你的项目名称,点击添加,选择新建筛选器即可。 ????????将文件加入到项目中也是一样的。 ????????这些做完后我们需要打开所有 .cpp 文件,并在顶部加上一行预编译头,保证能够编译通过。
#include "stdafx.h"
????????如下图所示:
2.2 验证导入效果
????????在 main 函数中加入以下测试代码,编译通过即可。
#include "stdafx.h"
#include <iostream>
#include <string>
#include "tinystr.h"
#include "tinyxml.h"
const std::string strXmlFile = "这里写一个xml文件所在位置,是绝对路径,不要直接照抄哦。";
int main()
{
TiXmlDocument *pDocument = new TiXmlDocument();
if (NULL == pDocument)
{
std::cout << "NULL == pDocument" << std::endl;
return -1;
}
if (!pDocument ->LoadFile(strXmlFile .c_str(), TIXML_ENCODING_UNKNOWN))
{
std::cout << "无法加载xml文件!" << std::endl;
return -1;
}
TiXmlElement* pRootElement = pDocument->RootElement();
if (NULL == pRootElement)
{
std::cout << "NULL == pRootElement" << std::endl;
return -1;
}
system("pause");
return 0;
}
????????如果没有 xml 文件的话就先用我的吧,新建一个文件文件,更改后缀为 .xml 即可。然后用记事本打开,写入以下内容并保存。
<?xml version="1.0" encoding="UTF-8" ?>
<excel version="1.0" author="huangzhihui">
<table id="1">
<colum id="1.1" name="Mike1" width="1" height="1" />
<colum id="1.2" name="John1" width="2" height="2" />
<colum id="1.3" name="Lucy1" width="3" height="3" />
</table>
<table id="2">
<colum id="2.1" name="Mike1" width="1" height="1" />
<colum id="2.2" name="John1" width="2" height="2" />
<colum id="2.3" name="Lucy1" width="3" height="3" />
</table>
</excel>
2.3 读取 xml 文件内容
void ReadXmlDocument(const std::string &strXmlFilePath)
{
if (strXmlFilePath.empty())
{
std::cout << "strXmlFilePath is empty!" << std::endl;
return;
}
TiXmlDocument *pDocument = new TiXmlDocument();
if (NULL == pDocument)
{
std::cout << "NULL == pDocument" << std::endl;
return ;
}
if (!pDocument->LoadFile(strXmlFilePath.c_str(), TIXML_ENCODING_UNKNOWN))
{
std::cout << "无法加载 xml 文件!" << std::endl;
return ;
}
pDocument->Print();
}
2.4 向 xml 文件中写入内容
void WriteXmlDocument(const std::string &strXmlFilePath)
{
if (strXmlFilePath.empty())
{
return;
}
TiXmlDocument *pDocument = new TiXmlDocument(strXmlFilePath.c_str());
if (NULL == pDocument)
{
return;
}
TiXmlDeclaration *pDeclaration = new TiXmlDeclaration("1.0", "UTF-8", "");
if (NULL == pDeclaration)
{
return;
}
pDocument->LinkEndChild(pDeclaration);
TiXmlElement *pRoot = new TiXmlElement("111");
if (NULL == pRoot)
{
return;
}
pRoot->SetAttribute("name", "111");
pDocument->LinkEndChild(pRoot);
TiXmlElement *pChildFir = new TiXmlElement("222");
pChildFir->SetAttribute("name", "222");
pChildFir->SetAttribute("age", "18");
pRoot->LinkEndChild(pChildFir);
pDocument->SaveFile(strXmlFilePath.c_str());
}
写入效果如下,看看是否符合你的预期呢:
3. 简单封装
????????根据标签的特征,抽取出一个基类,用于表示一个标签对象。
3.1 xml 标签节点基类 IXmlNode.h
#include "stdafx.h"
#include <string>
#include <vector>
#include "..\inc\tinystr.h"
#include "..\inc\tinyxml.h"
typedef std::vector<TiXmlAttribute *> TiXmlAttributes;
class IXmlNode
{
public:
virtual TiXmlElement *GetNode() = 0;
virtual std::string GetNodeName() = 0;
virtual TiXmlAttributes *GetNodeAttributes() = 0;
virtual int GetChildCount() = 0;
virtual int GetAttributesCount() = 0;
virtual bool InsertChildAt(IXmlNode *pInsertNode, int nIndex) = 0;
virtual bool DeleteChildAt(int nIndex) = 0;
virtual bool RemoveAllChilds() = 0;
virtual void SetNodeName(const std::string &strNodeName) = 0;
virtual std::string GetSpecifiedAttribute(const std::string &strAttriName) = 0;
virtual bool SetSpecifiedAttribute(const std::string &strAttriName, const std::string &strAttriValue) = 0;
virtual IXmlNode *GetChildAt(const int &nIndex) = 0;
virtual IXmlNode *GetChildByName(const std::string &strNodeName) = 0;
virtual IXmlNode *GetParent() = 0;
virtual bool SetParent(IXmlNode *pParent) = 0;
protected:
virtual bool InitXmlNodes(TiXmlElement *pInitNode, IXmlNode *pParent) = 0;
};
typedef std::vector<IXmlNode *> XmlNodeElements;
3.2 xml 标签节点实现 CXmlNode.h
#pragma once
#include "IXmlNode.h"
class CXmlNode : public IXmlNode
{
public:
CXmlNode(TiXmlElement *pInitNode, IXmlNode *pParent);
~CXmlNode();
public:
virtual TiXmlElement *GetNode() override;
virtual std::string GetNodeName() override;
virtual TiXmlAttributes *GetNodeAttributes() override;
virtual int GetChildCount() override;
virtual int GetAttributesCount() override;
virtual bool InsertChildAt(IXmlNode *pInsertNode, int nIndex) override;
virtual bool DeleteChildAt(int nIndex) override;
virtual bool RemoveAllChilds() override;
virtual void SetNodeName(const std::string &strNodeName) override;
virtual std::string GetSpecifiedAttribute(const std::string &strAttriName) override;
virtual bool SetSpecifiedAttribute(const std::string &strAttriName, const std::string &strAttriValue) override;
virtual IXmlNode *GetChildAt(const int &nIndex) override;
virtual IXmlNode *GetChildByName(const std::string &strNodeName) override;
virtual IXmlNode *GetParent() override;
virtual bool SetParent(IXmlNode *pParent) override;
public:
virtual XmlNodeElements *GetChildNodes();
virtual bool AddOrReplaceChilds(const XmlNodeElements &NewChilds);
protected:
virtual bool InitXmlNodes(TiXmlElement *pInitNode, IXmlNode *pParent) override;
private:
TiXmlElement *_pNode = NULL;
IXmlNode *_pParent = NULL;
XmlNodeElements _ChildNodes ;
TiXmlAttributes _Attributes ;
};
3.3 xml 标签节点实现 CXmlNode.cpp
#include "stdafx.h"
#include "CXmlNode.h"
CXmlNode::CXmlNode(TiXmlElement *pInitNode, IXmlNode *pParent)
{
InitXmlNodes(pInitNode, pParent);
}
CXmlNode::~CXmlNode()
{
if (_pNode != NULL)
{
_pNode = NULL;
}
if (_pParent != NULL)
{
_pParent = NULL;
}
if (!_ChildNodes.empty())
{
RemoveAllChilds();
}
if (!_Attributes.empty())
{
_Attributes.clear();
}
}
std::string CXmlNode::GetNodeName()
{
return std::string(_pNode->Value());
}
TiXmlAttributes *CXmlNode::GetNodeAttributes()
{
return &_Attributes;
}
XmlNodeElements *CXmlNode::GetChildNodes()
{
return &_ChildNodes;
}
int CXmlNode::GetChildCount()
{
return _ChildNodes.size();
}
int CXmlNode::GetAttributesCount()
{
return _ChildNodes.size();
}
bool CXmlNode::InsertChildAt(IXmlNode *pInsertNode, int nIndex)
{
if (pInsertNode == NULL || nIndex < 0 || nIndex >= GetChildCount())
{
return false;
}
_ChildNodes.insert(_ChildNodes.begin() + nIndex, pInsertNode);
return true;
}
bool CXmlNode::AddOrReplaceChilds(const XmlNodeElements &NewChilds)
{
if (NewChilds.empty())
{
return false;
}
RemoveAllChilds();
_ChildNodes = NewChilds;
return true;
}
bool CXmlNode::DeleteChildAt(int nIndex)
{
if (_ChildNodes.empty() || _ChildNodes.size() <= nIndex || nIndex < 0)
{
return false;
}
IXmlNode *pDeleteNode = _ChildNodes[nIndex];
_ChildNodes.erase(_ChildNodes.begin() + nIndex);
if (pDeleteNode != NULL)
{
delete pDeleteNode;
}
return true;
}
bool CXmlNode::RemoveAllChilds()
{
if (!_ChildNodes.empty())
{
for (int i = 0; i < _ChildNodes.size(); ++i)
{
IXmlNode *pTemp = _ChildNodes[i];
if (pTemp != NULL)
{
delete pTemp;
}
}
_ChildNodes.clear();
}
return true;
}
void CXmlNode::SetNodeName(const std::string &strNodeName)
{
_pNode->SetValue(strNodeName.c_str());
}
std::string CXmlNode::GetSpecifiedAttribute(const std::string &strAttriName)
{
if (_Attributes.empty())
{
return "Not Exist.";
}
for (int i = 0; i < _Attributes.size(); ++i)
{
if (_Attributes[i] == NULL)
{
continue;
}
if (_Attributes[i]->Name() == strAttriName)
{
return _Attributes[i]->Value();
}
}
return "Not Exist.";
}
bool CXmlNode::SetSpecifiedAttribute(const std::string &strAttriName, const std::string &strAttriValue)
{
if (_Attributes.empty())
{
return false;
}
for (int i = 0; i < _Attributes.size(); ++i)
{
if (_Attributes[i] == NULL)
{
continue;
}
if (_Attributes[i]->Name() == strAttriName)
{
_Attributes[i]->SetValue(strAttriValue.c_str());
return true;
}
}
return false;
}
IXmlNode *CXmlNode::GetChildAt(const int &nIndex)
{
if (_ChildNodes.empty() || _ChildNodes.size() <= nIndex || nIndex < 0)
{
return NULL;
}
return _ChildNodes[nIndex];
}
IXmlNode *CXmlNode::GetChildByName(const std::string &strNodeName)
{
if (_ChildNodes.empty() || strNodeName.empty())
{
return NULL;
}
for (int i = 0; i < _ChildNodes.size(); ++i)
{
if (_ChildNodes[i] == NULL)
{
continue;
}
if (_ChildNodes[i]->GetNodeName() == strNodeName)
{
return _ChildNodes[i];
}
}
return NULL;
}
IXmlNode *CXmlNode::GetParent()
{
return _pParent;
}
bool CXmlNode::SetParent(IXmlNode *pParent)
{
_pParent = pParent;
return true;
}
bool CXmlNode::InitXmlNodes(TiXmlElement *pInitNode, IXmlNode *pParent)
{
this->_pNode = pInitNode;
this->_pParent = pParent;
if (_pNode == NULL)
{
return false;
}
TiXmlAttribute *pAttr = _pNode->FirstAttribute();
while (NULL != pAttr)
{
_Attributes.push_back(pAttr);
pAttr = pAttr->Next();
}
for (TiXmlElement *StuElement = _pNode->FirstChildElement();
StuElement != NULL;
StuElement = StuElement->NextSiblingElement())
{
_ChildNodes.push_back(new CXmlNode(StuElement, this));
}
return true;
}
TiXmlElement *CXmlNode::GetNode()
{
return _pNode;
}
3.4 封装验证
int Test()
{
TiXmlDocument *_pDocument = new TiXmlDocument();
if (!_pDocument->LoadFile("..\\配置文件\\a.xml", TIXML_ENCODING_UNKNOWN))
{
std::cout << "无法加载xml文件!" << std::endl;
return -1;
}
TiXmlElement* pRootElement = _pDocument->RootElement();
if (pRootElement == NULL)
{
std::cout << "Can not get the root node of xml document." << std::endl;
return -1;
}
IXmlNode *_pRootNode = new CXmlNode(pRootElement, NULL);
if (NULL == _pRootNode)
{
return -1;
}
int nChildCount = _pRootNode->GetChildCount();
if (nChildCount != 1)
{
IXmlNode *pChildFir = _pRootNode->GetChildAt(0);
if (pChildFir == NULL)
{
std::cout << "Error" << std::endl;
return -1;
}
CXmlNode *pChildFirEx = dynamic_cast<CXmlNode *>(pChildFir);
std::cout << pChildFir->GetSpecifiedAttribute("id") << std::endl;
}
}
4. 整套操作流程封装
????????第3节只是将 xml 标签进行了封装,对于标签操作只需要使用我们自定义的对象即可,接口的多少及扩展都看自己喜好而定。然而我们还是要实现打开文件,获取根节点,并将根节点传入自定义对象中,也就是说我还想偷懒,干脆一步到位好了。那么接下来就是对文件和节点类进行封装。
4.1 IFileDocument.h 文档操作基类
#pragma once
#include <string>
class IFileDocument
{
public:
virtual void ReadDocument() = 0;
virtual void WriteDocument(const std::string &strDestFile) = 0;
};
4.2 CXmlDocument.h xml文档操作实现类
#pragma once
#include "IFileDocument.h"
#include "CXmlNode.h"
class CXmlDocument : public IFileDocument
{
public:
CXmlDocument(const std::string &strXmlFile);
~CXmlDocument();
public:
virtual void ReadDocument() override;
virtual void WriteDocument(const std::string &strDestFile) override;
public:
IXmlNode *GetRootNode();
private:
void CopyNodeAttributes(TiXmlElement *pRootSrc, TiXmlElement *pRootDest);
void CopyNodeChilds(IXmlNode *pRootSrc, TiXmlElement *pRootDest);
private:
std::string _strXmlFileName;
IXmlNode *_pRootNode;
TiXmlDocument *_pDocument;
};
4.3 CXmlDocument.cpp
#include "stdafx.h"
#include "CXmlDocument.h"
CXmlDocument::CXmlDocument(const std::string &strXmlFile)
{
_strXmlFileName = strXmlFile;
_pRootNode = NULL;
_pDocument = NULL;
}
CXmlDocument::~CXmlDocument()
{
if (_pRootNode != NULL)
{
delete _pRootNode;
}
if (_pDocument != NULL)
{
delete _pDocument;
}
}
void CXmlDocument::ReadDocument()
{
_pDocument = new TiXmlDocument();
if (!_pDocument->LoadFile(_strXmlFileName.c_str(), TIXML_ENCODING_UNKNOWN))
{
std::cout << "无法加载xml文件!" << std::endl;
return;
}
TiXmlElement* pRootElement = _pDocument->RootElement();
if (pRootElement == NULL)
{
std::cout << "Can not get the root node of xml document." << std::endl;
return;
}
_pRootNode = new CXmlNode(pRootElement, NULL);
}
void CXmlDocument::WriteDocument(const std::string & strDestFile)
{
if (strDestFile.empty())
{
return;
}
TiXmlDocument *pDocument = new TiXmlDocument(strDestFile.c_str());
if (NULL == pDocument)
{
return;
}
TiXmlDeclaration *pDeclaration = new TiXmlDeclaration("1.0", "UTF-8", "");
if (NULL == pDeclaration)
{
return;
}
pDocument->LinkEndChild(pDeclaration);
TiXmlElement *pRootSrc = GetRootNode()->GetNode();
if (pRootSrc == NULL)
{
return;
}
TiXmlElement *pRoot = new TiXmlElement(pRootSrc->Value());
if (NULL == pRoot)
{
return;
}
pDocument->LinkEndChild(pRoot);
CopyNodeChilds(GetRootNode(), pRoot);
pDocument->SaveFile(strDestFile.c_str());
}
IXmlNode * CXmlDocument::GetRootNode()
{
if (_pRootNode == NULL)
{
ReadDocument();
}
return _pRootNode;
}
void CXmlDocument::CopyNodeAttributes(TiXmlElement *pRootSrc, TiXmlElement *pRootDest)
{
if (pRootSrc == NULL || pRootDest == NULL)
{
return;
}
TiXmlAttribute *pAttr = pRootSrc->FirstAttribute();
while (NULL != pAttr)
{
pRootDest->SetAttribute(pAttr->Name(), pAttr->Value());
pAttr = pAttr->Next();
}
}
void CXmlDocument::CopyNodeChilds(IXmlNode *pRootSrc, TiXmlElement *pRootDest)
{
if (pRootSrc == NULL || pRootDest == NULL)
{
return;
}
CXmlNode *pRootSrcEx = dynamic_cast<CXmlNode *>(pRootSrc);
if (pRootSrcEx == NULL)
{
return;
}
CopyNodeAttributes(pRootSrcEx->GetNode(), pRootDest);
XmlNodeElements *pChilds = pRootSrcEx->GetChildNodes();
for (int i = 0; i < pChilds->size(); ++i)
{
TiXmlElement *pTempSrc = pChilds->at(i)->GetNode();
if (pTempSrc == NULL)
{
continue;
}
TiXmlElement *pTempDest = new TiXmlElement(pTempSrc->Value());
if (pTempDest == NULL)
{
continue;
}
CopyNodeAttributes(pTempSrc, pTempDest);
CopyNodeChilds(pChilds->at(i), pTempDest);
pRootDest->LinkEndChild(pTempDest);
}
}
4.4 封装验证
int Test()
{
CXmlDocument xmlDoc("..\\配置文件\\a.xml");
IXmlNode *pRootNode = xmlDoc.GetRootNode();
if (pRootNode == NULL)
{
std::cout << "Error" << std::endl;
return -1;
}
std::string strNodeName = pRootNode->GetNodeName();
IXmlNode *pChildEle = pRootNode->GetChildAt(0);
if (pChildEle == NULL)
{
std::cout << "Error1" << std::endl;
return -1;
}
xmlDoc.WriteDocument("..\\配置文件\\b.xml");
}
|