(从EEPROM到云服务器的数据存取)
xml是Extensible Markup Language可扩展标记语言的简写,实际上可以把xml文件当成一个微型的数据库,也就是说它是用来存取数据的。我们知道内存里面的数据掉电了就没有了,在单片机开发的时候如果有数据需要掉电保存我们通常会保存在内部或者外部的EEPROM或者flash中,由于单片机开发的数据量非常小,所以我们读写数据的时候都是按自己的规则来规定那个字节是什么数据。在一些linux或者其他系统的应用程序中也需要保存一些数据,比如配置文件或者断点记忆等,这个时候我们也可以用一个纯文本文件按照我们自己的规则来存取数据,但是为了统一接口,我们必须约定一个统一的规则或者格式,这便产生了xml文件。然而xml文件虽然能够快速方便的存取数据,但是它并不能描述数据之间的相互关系,也不能在存取的时候对数据进行一些控制和筛选等操作,所以对于一些数据量很大并且数据之间的关系错综复杂而又需要这种数据间的关系和对数据进行一些控制的应用,如果仍然使用xml文件来进行数据存储的话,那么应用程序的数据处理部分就会相当复杂。为了解决这个问题,数据库就应运而生,数据库不仅可以存取数据,而且可以很好的描述数据间的相互关系,还可以对数据进行控制操作。不过数据库虽然弥补了xml的不足,并拥有了诸多优点,但是处理数据间的关系,对数据进行遍历查找等工作使得计算量大大增加,并且很多时候为了节省存储空间通常会把数据库部署在服务器上,很多客户端的程序共用一个数据库,这样一来多用户的并发控制又是一个问题了,为了解决这些问题,于是又出现了数据库系统。自定义存取方式、xml、数据库和数据库系统并没有孰优孰劣之分,具体使用哪个要根据实际需求来进行选择。【个人观点】
xml文件结构说明:
每个xml文件的第一行都是xml文件说明,一般说明了xml的版本和编码信息;
如:<? xmlversion="1.0" encoding="UTF-8"?>
第二行为xml的起始节点,也是根节点
Opencv的xml文件默认的根节点为:<opencv_storage>……</opencv_storage>
每个xml文件有且只有一个根节点,其他节点都包含在这个根节点之内,每个节点又可以包含若干个子节点。这一点跟linux的目录结构很相似,linux文件系统有且仅有一个跟目录"/",其他目录都是挂载在这个根目录中的,每个目录又可以包含若干个子目录。
1.xml文件的写和读
下面代码写数据部分首先以可写的方式打开了一个test.xml文件,然后写入一个Mat矩阵,最后关闭。读数据部分首先以可读方式打开了一个test.xml文件,然后读出“src1”节点里面的中间,最后关闭。
//========建立节点(写数据)========= Mat src=(Mat_<double>(3,3)<<1,2,3,4,5,6,7,8,9); FileStorage fswrite("test.xml",FileStorage::WRITE); fswrite<<"src1"<<src; fswrite.release(); cout<<"Write Finished"<<endl; //========遍历节点(读数据)========= FileStorage fsread("test.xml",FileStorage::READ); Mat dst; fsread["src1"]>>dst; cout<<dst<<endl; fsread.release(); cout<<"Reaed Finished"<<endl;
其中读和写打开文件也可以是下面那样:
FileStorage fswrite; fswrite.open("test.xml",FileStorage::WRITE); FileStorage fsread; fsread.open("test.xml",FileStorage::READ);
需要注意的是,FileStorage::WRITE每次都会新建一个文件,如果文件已经存在就会被覆盖掉,如果不想被覆盖,而是继续在已有的文件基础上添加内容,那么可以以FileStorage::APPEND的方式来打开!
2.vector和maps结构
vector:xml文件节点,不包含子节点。
map:xml的节点,包含子节点。
在输入vector的开始和结尾要分别输入“[”,“]”,而在输入map结构的开始和结尾要分别写入“{”,“}”,此外在输入vector和map前都要先输入标签名称。
下面代码为手动创建一个xml父节点(map),包含三个子节点(vector)。
//========建立节点(写数据)========= FileStorage fswrite; fswrite.open("test.xml",FileStorage::WRITE); fswrite<<"src"<<"{"<<"src1"<<"["<<1<<2<<3<<"]"//子节点 <<"src2"<<"["<<1<<2<<3<<"]" <<"src3"<<"["<<1<<2<<3<<"]"<<"}"; fswrite.release(); cout<<"Write Finished"<<endl;
产生的xml文件如下:
<?xmlversion="1.0"?>
<opencv_storage>
<src>
<src1>
1 2 3</src1>
<src2>
1 2 3</src2>
<src3>
1 2 3</src3></src>
</opencv_storage>
3.遍历xml节点
下面代码首先创建了一个test.xml的文件,然后像里面写入了五个Mat矩阵,最后打开这个文件,再遍历每一个节点并打印出来。
遍历的时候,先获取了文件的根节点,然后定义了一个迭代器,从根节点的第一个节点开始遍历,直到最后一个节点。
//========建立节点(写数据)========= Mat src=(Mat_<double>(3,FileStorage::WRITE); fswrite<<"src1"<<src; fswrite<<"src2"<<src; fswrite<<"src3"<<src; fswrite<<"src4"<<src; fswrite<<"src5"<<src; fswrite.release(); cout<<"Write Finished"<<endl; //========遍历节点(读数据)========= FileStorage fsread("test.xml",FileStorage::READ); FileNode rootnode=fsread.root(); FileNodeIterator it; for(it=rootnode.begin();it<rootnode.end();it++){ Mat dst; (*it)>>dst; //it>>dst;//也可以 cout<<dst<<endl; } cout<<"Reaed Finished"<<endl;
结果分析:
下面是test.xml的内容,其中<?xml version="1.0"?>为文件信息,说明了xml的版本为1.0;<opencv_storage>...</opencv_storage>为根节点,<src1 type_id="opencv-matrix">...</src1>为第一级节点(根节点的子节点),<rows>3</rows>为第二级节点,它是<src1 type_id="opencv-matrix">...</src1>的子节点。
<?xmlversion="1.0"?>
<opencv_storage>
<src1type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src1>
<src2type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src2>
<src3type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src3>
<src4type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src4>
<src5type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src5>
</opencv_storage>
下面为终端打印信息:
4.自定义结构存储
Opencv的xml可以轻松实现int,float,double,string,mat等数据类型的存储和读取,但是往往我们需要保存自定义类型的数据,要实现自定义数据的存取,需要完成以下步骤,第一自定义数据,第二操作符重载。
/************************* //自定义类 *************************/ class faceInfo{ public: //写入数据【1】 void write(FileStorage& fs) const { fs<<"{"<<"matrix"<<matrix<<"label"<<label<<"}"; } //读出数据【2】 void read(const FileNode& node) { node["matrix"]>>matrix; node["label"]>>label; } public: Mat matrix; string label; }; /************************* //由操作符“<<”调用【3】 *************************/ void write(FileStorage& fs,const std::string&,const faceInfo& x) { x.write(fs); } /************************* //由操作符“>>”调用【4】 *************************/ void read(const FileNode& node,faceInfo& x,const faceInfo& default_value = faceInfo()) { if(node.empty()) x = default_value; else x.read(node); }
以上代码定义了一个faceInfo类,为了用”>>”,”<<”这两个符号进行读写操作,代码中【1】【2】【3】【4】4个函数是必须要实现的,定义好了之后就可以进行读写了,有一点非常值得注意,先看代码在分析。
int main(int argc,char *argv[]) { QCoreApplication a(argc,argv); //========建立节点(写数据)========= Mat src=(Mat_<double>(3,9); faceInfo face1={src,"Jack"};//faceInfo为自定义类 faceInfo face2={src,"John"}; faceInfo face3={src,"Mike"}; FileStorage fswrite("test.xml",FileStorage::WRITE); fswrite<<"face1"<<face1<<"face2"<<face2<<"face3"<<face3; fswrite.release(); cout<<"Write Finished"<<endl; //========遍历节点(读数据)========= FileStorage fsread("test.xml",FileStorage::READ); FileNode rootnode=fsread.root();//获取根节点 if(rootnode.type()!=FileNode::MAP){ cout<<"This is not map !"<<endl; } FileNodeIterator it=rootnode.begin(); while(it<rootnode.end()){ faceInfo dst; it>>dst; //(*it)>>dst;//不行 cout<<dst.label<<endl; cout<<dst.matrix<<endl; } cout<<"Reaed Finished"<<endl; fsread.release(); return a.exec(); }
这里有两个地方要特别注意的:
(1).比较这里的遍历节点部分的代码和“第2遍历xml节点”中的代码就会发现,在遍历之前的代码中用的是(*it)>>dst,而这里的代码用的是it>>dst!
(2).还有个不同点就是,之前代码里面每次遍历完都有it++,而这里却没有!
原因分析:
我们故意把voidread(constFileNode&node,faceInfo&x,constfaceInfo&default_value=faceInfo());这个函数写错变成voidread(constFileNode&node);通过编译器报错我们可以找到调用它的函数,如下所示,就是我们重载的操作符”>>”
template<typename_Tp>staticinlineFileNodeIterator&operator>>(FileNodeIterator&it,_Tp&value)
{read(*it,value,_Tp());return++it;}
从上面可以看到,我们重载的操作符”>>”内部对迭代器it进行了*it的操作,如果我们在外部在进行*it的操作,那么迭代器将会指向当前的下一级节点,这并不是我们想要的。另外在内部还进行了++it的操作,所以我们如果在外部再进行it++就会跟这里重复。至于为什么默认的数据类型it和(*it)为什么都可以,这个我也没找到原因~~。
代码运行结果:
<?xmlversion="1.0"?>
<opencv_storage>
<face1>
<matrixtype_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3.4. 5. 6. 7. 8. 9.</data></matrix>
<label>Jack</label></face1>
<face2>
<matrix type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3.4. 5. 6. 7. 8. 9.</data></matrix>
<label>John</label></face2>
<face3>
<matrixtype_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3.4. 5. 6. 7. 8. 9.</data></matrix>
<label>Mike</label></face3>
</opencv_storage>
原文链接:https://www.f2er.com/xml/295784.html