源代码获取与联系作者
如果你需要源代码,或者你在学习本教程的过程中遇到任何问题以及本教程出现任何错误,请务必通过邮箱T_Genius_8@qq.com联系我,来信请于主题处简要介绍你的身份(如需源代码)与目的。
一、xml文件的格式
xml文件示例:
1.文本类型xml文件 input.xml
注:本文件为两段式文本
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE imulation>
<allistic>
<paramclass name="仿真参数">
<param value="0.000002” name="仿真时间步长(s)" type="double"/>
note=""type="double"/>
</paramclass>
<paramclass name="计算参数">
<param value="0.402" name="Imd"type="double"/>
<param value="1200" name="c"type="double"/>
<param value="240000" name="E"type="double"/>
<param value="668e26” name="A"type="double"/>
<param value="8.31451" name="R"type="double"/>
</paramclass>
</allistic>
-
表格类型xml文件output.xml
注:本文件为两段式文本,第一段为表格(type=”table“),第二段为文本(type=”default“不显示)
<?xml version="1.0"?> <!DOCTYPE IBSimulation> -<IBallistic> -<paramclass type="table" name="仿真结果"> <row value="L(mm),T(K),"/> <row value="double,double"/> <row value="0.000000,850.035771"/> <row value="0.000885,847.861889"/> <row value="0.001770,844.633632"/> <row value="0.002656,840.575696"/> <row value="0.003541,835.911189"/> <row value="0.004426,830.839636"/> <row value="0.005311,825.525987"/> </paramclass> <paramclass name="计算结果"> <param value="850”name="最大值”type="double"/> </paramclass> </IBallistic>
对于xml内容的介绍:
在文本段类型中,每个paramclass为一段,每个param为一行,即数据以paramclass为一组,每个param为一种数据。如intput.xml中,第一组数据名为仿真参数,其中包括时间步长、空间步长等参数
<paramclass value="”name="仿真参数">
<param value="5”name="长度(mm)”note=""type="double"/>
<param value="0.000002”name="仿真时间步长(s)”type="double"/>
<param value=”由空间步长计算”name="仿真空间步长”note=""type="double"/>
</paramclass>
在output.xml中,则是第一组为table类型,每一行为raw,第二组为文本类型,可以按第一组的方法书写。
二、xml在c++中需要的库
头文件部分
tinyxml.h、ibrwxml.h、mydatatype.h、strprocess.h、targetver.h、tinystr.h
函数文件部分
strprocess.cpp、tinystr.cpp、tinyxml.cpp、tinyxmlerror.cpp、tinyxmlparser.cpp、ibrwxml.cpp
在以上文件中,最重要的有三个文件,分别是ibrwxml.h、ibrwxml.cpp以及mydatatype.h,其余文件为需要的附件,如转义文件。
其中ibrwxml.h管理着数据结构的类型,即数据类型是private(私有)还是public(公开),这关系到数据能否向外通信。
class ibrwxml
{
private:
xxx;
xxx;//此处xxx不具有任何意义,仅作为列举
public:
ibrwxml(void);
~ibrwxml(void);
yyy;
yyy;//此处xxx不具有任何意义,仅作为列举
};
此处展示了ibrwxml.h中的格式,此处定义了一个名为ibrwxml的class容器,对于xml数据中private类型的数据(如xxx)的操作只能在该容器中进行,但是public类型的数据(如yyy)则可以与外界通信。在后续读取与写入代码中会对代码做详细展示。
在mydatatype.h中,管理着class中的数据,上述的ibrwxml.h仅是对数据类型做了定义,在mydatatype.h中则是定义变量名及数据类型,展示如下
typedef struct{
double S; //横截面积(m^2);
double V0; //容积(m^3);
double Pj; //压力(MPa);
vector<double> Lv; //行程
vector<double> Fc; //阻力
}SGun;
显示,代码定义了一个数据类型为SGun的一组变量,其中double S;
为SGun下一个名为d的double类型数据, vector<double> Lv; //行程
为SGun下一个名为Lv的double类型的向量,对于其调用会在后文做详细介绍。
在ibrwxml.cpp中则是定义了对xml文件的操作,包括读取文本类型xml数据,读取表格类型xml数据,写入文本类型xml数据,写入表格类型xml数据(注:需自己辨别)
其中读取操作代码如下,无需改动,具体调用方式在后文会有详细介绍
bool ibrwxml::LoadInputXmlByOrder(const char* path,MyDataNode& dataNode)//读写*.xml文件
{
MyDataNode results;
bool ret = ReadInputXmlUniversalMethod(results,path);
XXX;
xxx;
return true;
}
写入代码如下,因涉及到具体的写入内容,在后文我会做详细介绍。
void ibrwxml::WriteOutputXml(const char *FileName)
{
xxx;
xxx;
xxx;
}
三、xml文件的读取
对xml文件的读取分为两个部分:1.读取xml文件的函数;2.如何在程序中调用该函数
首先介绍对xml文件的读取函数,此函数依然位于ibrwxml.cpp中,修改该函数内容来控制读取xml中的数据。
对于文本数据的读取:
//解析仿真参数
void ibrwxml::parse_SimPara(MyDataNode& tp)
{
if (strcom.UTF8ToGBK(tp.attributes["name"]) == "仿真参数")
{
for (int i = 0;i < tp.childNodes.size();++i)
{
if (strcom.UTF8ToGBK(tp.childNodes[i].attributes["name"]) == "仿真时间步长(s)")
{
MyoutSimPara.timestep = atof(strcom.UTF8ToGBK(tp.childNodes[i].attributes["value"]).c_str());
}
}
}
//示意区
if (strcom.UTF8ToGBK(tp.attributes["name"]) == "yyy")
{
for (int i = 0;i < tp.childNodes.size();++i)
{
if (strcom.UTF8ToGBK(tp.childNodes[i].attributes["name"]) == "xxx")
{
xxx;
}
if (strcom.UTF8ToGBK(tp.childNodes[i].attributes["name"]) == "xxx")
{
xxx;
}
}
}
//
}
对于表格类型数据的读取:
void ibrwxml::parse_Para(MyDataNode& tp)
{
if (strcom.UTF8ToGBK(tp.attributes["name"]) == "xxx")//段落名字
{
for (int i = 0;i < tp.childNodes.size();++i)
{
if (strcom.UTF8ToGBK(tp.childNodes[i].attributes["name"]) == "xxx")//分节点名字
{
vector<double> Rowvalue; //行拆分的字符
if (tp.childNodes[i].childNodes.size() > 2)//如果行数大于2,即去表头操作
//一般表的第一行为名称,第二行为数据类型,在提取数据时需要去除
{
for (int j = 2;j < tp.childNodes[i].childNodes.size();++j)
{
string row = tp.childNodes[i].childNodes[j].attributes["value"];
Rowvalue = strcom.splitto1D(row, ",");
My.D1.push_back(Rowvalue[0]);
My.D2.push_back(Rowvalue[1]);
//示例中是针对2列的表格,编程中根据实际情况增减
}
}
else
{
cout<<"There are no data!"<<endl;
}
}
}
}
}
上述代码的写作逻辑是先判断paramclass中name标签是否符合,再循环判断param中name标签,如果true,将其value赋值给相应的数据名称(xml文件中value为文本,非数值,因此需要UTF8ToGBK转义)。
重要注MyoutSimPara.timestep=atof(strcom.UTF8ToGBK(tp.childNodes[i].attributes["value"]).c_str());
中,你会在实例中见到atof与atoi两种函数,其中atof是将value读取成float类型的数据,即浮点数;atoi是将value读取成int类型的数据,即整数,在使用中需严格区分。
注:对于文本类型和表格类型在同一段,即同一个paramclass下的情况二者可以合并读取函数到一个函数中
在其它程序中调用读取函数:
首先,我们要定义一个ibrwxml类型的class容器,第二章的介绍你可以视作一个通用函数,现在函数有了,但是还没有输入参数,因此,我们要定义一个符合函数输入参数要求的数据,即定义一个ibrwxml类型的class容器。代码如下:
ibrwxml cpp1;
MyDataNode datein;//此为本代码必要写法,但无任何实际意义
cpp1.LoadInputXmlByOrder("input.xml", datein);
cpp1.xxx意味着我们的操作都是在cpp1这个ibrwaml类型的class容器中进行的,因此不需要考虑与外界的通信,即上文提到的private类型数据和public类型数据在其中均可进行操作。input.xml为需要读取的xml文件名,其中“ ”代表这是字符串类型。
在实际的代码写作中,我们会遇到xml文件或路径为中文的问题,具体改进代码如下:
int main()
{
//读取xml文件
ibrwxml cpp1;
MyDataNode datein;
strprocess strp;
cpp1.LoadInputXmlByOrder((char*)strp.UTF8ToGBK("input.xml").data(), datein);
xxx;
xxx;
xxx;
xxx;
return 0;
}
注:此处赋值操作涉及数据向class外进行通信,因此需要使用的数据类型为public类型,否则无法进行通信。
为了代码的美观和易于管理,我们往往将上述读取程序打包成一个函数,在其余函数使用时只需调用该即可(本文提供的源代码未涉及)
如上文部分打包为函数后function.cpp代码如下:
void run(const char* inputxml,PFun fun)
{
ibrwxml cpp1;
MyDataNode datein;
strprocess strp;
cpp1.LoadInputXmlByOrder((char*)strp.UTF8ToGBK(inputxml).data(), datein);
xxx;
xxx;
xxx;
return;
}
void fun(char*)
{
}
四、xml文件的写入
下面我们来介绍xml文件的写入操作,即将你的结果写进xml文件,示例代码如下
void ibrwxml::WriteOutputXml(const char *FileName)
{
vector<string> row;
vector<string> row3;
char crow[1000];
char c[1000];
string rowval,row1,row2;
MyDataNode rowNode;
//-------------仿真计算结果-------------------
row1=strcom.GBKToUTF8("L(mm),T(K),");
row2=strcom.GBKToUTF8("double,double");
MyDataNode rowNode1,rowNode2;
rowNode1.nodeName="row";
rowNode1.attributes["value"]=row1;
rowNode2.nodeName="row";
rowNode2.attributes["value"]=row2;
MyDataNode paramClassNode1;
paramClassNode1.nodeName="paramclass";
paramClassNode1.attributes["type"] = "table";
paramClassNode1.attributes["name"]=strcom.GBKToUTF8("仿真结果");
paramClassNode1.childNodes.push_back(rowNode1);
for (int i = 0;i < sizeof(MyOutParam.L) / sizeof(MyOutParam.L[0]);++i)
{
if (MyOutParam.T[i] > 0) {
strcpy_s(crow, "");
sprintf_s(crow, "%f%s%f", MyOutParam.L[i], ",", MyOutParam.T[i]);
row.push_back(crow);
}
}
for (int i = 0; i < row.size(); ++i)
{
rowNode1.nodeName = "row";
rowNode1.attributes["value"] = row[i];
paramClassNode1.childNodes.push_back(rowNode1);
}
MyDataNode dataNode;
dataNode.nodeName="allistic";
dataNode.childNodes.push_back(paramClassNode1);
CreateOutputXmlUniversalMethod(dataNode,FileName);
}
其中,主要的输出内容为MyOutParam.L[i], ",", MyOutParam.T[i]
,在mydatatype中已经将MyOutParam类型定义为public,因此在MyOutParam类型中定义的变量(L、T)可以直接输出(即与外界通信)。
sprintf_s语句中"%f%s%f"
要与输出内容对应,即%浮点数%字符串%浮点数
对应 MyOutParam.L[i], ",", MyOutParam.T[i]
在上述代码中,dataNode.nodeName="allistic";
管理了输出xml的根节点名字,可以修改(无所谓)
MyDataNode paramClassNode1;
paramClassNode1.nodeName="paramclass";
paramClassNode1.attributes["type"] = "table";
paramClassNode1.attributes["name"]=strcom.GBKToUTF8("仿真结果");
paramClassNode1.childNodes.push_back(rowNode1);
xxx;
xxx;
xxx;
dataNode.childNodes.push_back(paramClassNode1);
CreateOutputXmlUniversalMethod(dataNode,FileName);
这部分是对paramclass进行命名和定义类型。
row1=strcom.GBKToUTF8("L(mm),T(K),");
row2=strcom.GBKToUTF8("double,double");
MyDataNode rowNode1,rowNode2;
rowNode1.nodeName="row";
rowNode1.attributes["value"]=row1;
rowNode2.nodeName="row";
rowNode2.attributes["value"]=row2;
row1,row2是作为表头的固定的输出,最后就形成了这样的xml结果文件
<?xml version="1.0"?>
<!DOCTYPE IBSimulation>
-<allistic>
-<paramclass type="table" name="仿真结果">
<row value="L(mm),T(K),"/>
<row value="double,double"/>
<row value="0.000000,850.035771"/>
<row value="0.000885,847.861889"/>
<row value="0.001770,844.633632"/>
<row value="0.002656,840.575696"/>
<row value="0.003541,835.911189"/>
<row value="0.004426,830.839636"/>
<row value="0.005311,825.525987"/>
</paramclass>
</allistic>
在写代码时,我也建议将输出部分代码封装成函数,示例如下:
void run(const char* outputxml, PFun fun)
{
ibrwxml cpp1;
strprocess strp;
xxx;
xxx;
xxx;
cpp1.WriteOutputXml((char*)strp.UTF8ToGBK(outputxml).data());
return;
}
其实,我们可以同时将输入与输出一起封装成function.cpp文件
相应地,在main函数中我们只需要通过简单的代码即可调用
#include<iostream>
#include"function.h";
using namespace std;
int main() {
run1("input.xml",fun);
xxx;
xxx;
xxx;
run("output.xml", fun);
return 0;
}