Qt读写XML文档
什么是XML?
XML(eXtensible Markup Language)是一种通用的文本格式,与数据库一样被广泛运用于数据交换和数据存储,但在使用场景上二者有着巨大的差别,这是由于自身属性决定的。与数据库相比,XML缺少数据库具备的特性:高效的存储、索引和数据修改机制;严格的数据安全访问控制;完整的事务和数据一致性控制;多用户访问机制;触发器、完善的并发控制等。因此,用户量大、数据集成度高以及性能要求高的数据环境中还是需要数据库来完成任务,可以进行大量数据的存储和分析。
而xml是一种存储数据的标准格式,是为了便于网络数据传输和交互,解决的是数据在网上传输标准的问题,把原来各种各样的数据孤岛可以通过xml这座桥梁连接起来,而存在的期性能和存储的数据量,严重受着文件的大小而影响,存储xml的文件越大传递和读取起来越耗时。
所以打个比方,数据库就好比是盛数据的桶,而xml则是数据传输转换的桥梁,所以数据库是数据库,xml是xml,二者是截然不同的,当然二者也存在非常紧密的联系,毕竟都是处理数据的工具。 以上部分主要参考King-Blog的博客
Qt对于XML的支持
Qt 提供提供了Qt XML模块来进行XML文档的处理,主要提供了DOM方法和SAX方法。
DOM(Document Object Model)即文档对象模型,提供了一个接口来访问和改变一个XML文件的内容、结构,可以将XML文档表示为一个存储在内存中具有层次的树形视图,使用DOM可以很方便地进行XML的随机访问与增删改查,这是他最大的优点。与此同时带来的问题是需要一次性将整个XML文档读入内存,会占用很大的内存。
SAX(Simple API for XML)为XML解析器提供了一个基于事件的标准接口。如果不需要对文档进行操作,只需要读取整个XML文档,那么使用SAX方法最高效。不过他只能读取XML文档。 此外,从Qt4.3开始引入了两个新的类来读取和写入XML文档:QXmlStreamReader和QXmlStreamWriter,一种快速的基于流的方式访问良格式 XML 文档。它是作为SAX解析器的替代品身份出现的,它比SAX解析器更快更方便。本文介绍DOM方法与XML流方法。
DOM方法读写XML
Dom方法读XML
<?xml version="1.0" encoding="UTF-8"?>
<Begin>
<Type1 myLover="yxx">
<sex>girl</sex>
<school>XiAn</school>
<age>1996</age>
<special>beautiful</special>
<relationship>classmate</relationship>
<duration>2years</duration>
</Type1>
<Type2 myLover="83">
<aa>woceng</aa>
<bb>aiguo</bb>
<cc>yeshiqu</cc>
<dd>guo</dd>
<ee>changguo</ee>
<ff>aide</ff>
</Type2>
<Type3>
<yy>tianyuse</yy>
<xx>521</xx>
<xx>0803</xx>
<ll>!!!</ll>
<oo>give</oo>
<vv>me</vv>
<ee>hug</ee>
</Type3>
</Begin>
一个XML文件如下所示,首先应该搞清楚几个常用的术语。以下代码中, 第一行说明该文件的版本号和编码;"begin"是根节点,可以理解为一级目录; ”TypeX“是子节点,可以理解为二级目录; ”myLover“是属性,”yxx“是属性值,属性值必须在双引号之内,属性写在尖括号内,多个属性时,也要全部写在尖括号内; “sex”“school”等是三级节点,可以理解为三级目录; ”girl“”“Xian”等是文本; 在使用DOM读取之前,先看一下在XML模块中的Qt方法与该xml文件之间的对应关系。 下面在贴上使用DOM读的效果图,上述xml中的信息都可以被获取,可与上幅图片对比看。 在介绍源码之前,有几点说明如下。 1.在映射图中可以清楚的看到,除了一级目录外的次级目录(包括二级、三级以及之后的)的节点都保存在QDomNode类中,但是节点的属性、属性值、文本都只有在QDomElement类中才提供读取的方法。因此,读取节点的属性(值)以及文本需要将QDomNode类转换成QDomElement,通常的做法是,使用childNodes方法,获取次级目录中节点的个数,将其保存在QDomNodeList中,遍历这个列表,将每一个索引值都转换成QDomElement进而获取属性(值)、文本。 2.使用QDomDocument类之前先在pro文件中写入 QT += xml 3.本案例中由于疏忽直到写到现在才发现,未在三级目录中添加属性值的读取,不过不妨大碍,与二级目录中读取属性(值)的方法一致。 4.str.toUtf8().data(),这个操作将会有QString类型转换成char *类型,这样做的好处是,使用qDebug打印数据到控制台时,将不带双引号。 5.DOM读的源码是,将读功能封装成了一个函数,函数执行时,会弹出文件选择框,过滤除xml格式的其他文件,用户打开xml将在控制台打印各级目录信息,属性信息以及文本信息。上述效果截图中正是打开的上述的xml文件。
void Widget::domRead()
{
QString fileName = QFileDialog::getOpenFileName(this,"get xml file",
"C:/Users/Jack_Chen/Desktop","xml(*.xml)");
if(fileName.isEmpty()){
return;
}
myFile->setFileName(fileName);
bool ret =myFile->open(QIODevice::ReadOnly|QIODevice::Text);
if(!ret){
QMessageBox::warning(this,"warning","打开文件失败!");
return;
}
QDomDocument doc("yxx");
bool isLot = doc.setContent(myFile);
if(!isLot){
myFile->close();
QMessageBox::warning(this,"waring","关联文件失败");
return;
}
myFile->close();
QDomElement firstElem = doc.documentElement();
qDebug()<<"一级目录<begin>: "<<firstElem.nodeName();
QDomNodeList secondList = firstElem.childNodes();
for (int i = 0; i < secondList.count(); ++i) {
QString str = QString(" 第 %1 个二级目录节点:").arg(i+1);
qDebug()<<str.toUtf8().data()<<secondList.at(i).nodeName();
QDomElement secondElem = secondList.at(i).toElement();
if(secondElem.hasAttribute("myLover")){
qDebug()<<" "<<secondElem.nodeName().append("的属性值myLover为").toUtf8().data()<<secondElem.attribute("myLover");
}
else{
qDebug()<<" "<<secondElem.nodeName().append("该节点无属性值").toUtf8().data();
}
QDomNodeList thirdList = secondList.at(i).childNodes();
for(int j = 0;j<thirdList.length();j++ ){
QDomElement thirdElement=thirdList.at(j).toElement();
QString str_1 = QString(" %1中第 %2 个三级目录节点 %3,他的文本值为 %4:")
.arg(secondList.at(i).nodeName(),QString::number(j+1),thirdList.at(j).nodeName(),thirdElement.text());
qDebug()<<str_1.toUtf8().data();
}
}
Dom方法写XML
写XML文档应该按照XML自身的格式来写,首先写第一行关于版本和编码的描述,然后加入各级目录节点的同时,加入与节点对应的属性值和文本。最后使用QDomDocument类提供的save方法,将QTextStream类与文件关联进行保存。源码部分与效果图粘贴如下。
void Widget::domWrite()
{
QDomDocument doc;
QDomProcessingInstruction instruction = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
doc.appendChild(instruction);
QDomElement firstNode = doc.createElement("Begin");
doc.appendChild(firstNode);
QDomElement secondNode = doc.createElement("Type");
QDomAttr secondAttr = doc.createAttribute("MyLover");
secondAttr.setValue("yxx");
secondNode.setAttributeNode(secondAttr);
firstNode.appendChild(secondNode);
QDomElement thirdNode_1 = doc.createElement("sex");
secondNode.appendChild(thirdNode_1);
QDomText t1 = doc.createTextNode("girl");
thirdNode_1.appendChild(t1);
QDomElement thirdNode_2 = doc.createElement("school");
secondNode.appendChild(thirdNode_2);
QDomText t2 = doc.createTextNode("Xian");
thirdNode_2.appendChild(t2);
QDomElement thirdNode_3 = doc.createElement("age");
secondNode.appendChild(thirdNode_3);
QDomText t3 = doc.createTextNode("1996");
thirdNode_3.appendChild(t3);
QDomElement thirdNode_4 = doc.createElement("special");
secondNode.appendChild(thirdNode_4);
QDomText t4 = doc.createTextNode("beautiful");
thirdNode_4.appendChild(t4);
QString fileName = QFileDialog::getSaveFileName(this,"Save xml file",
"C:/Users/Jack_Chen/Desktop","xml(*.xml)");
if(fileName.isEmpty()){
return;
}
myFile->setFileName(fileName);
bool ret =myFile->open(QIODevice::WriteOnly|QIODevice::Text);
if(!ret){
QMessageBox::warning(this,"warning","保存文件失败!");
return;
}
QTextStream stream(myFile);
stream.setCodec("UTF-8");
doc.save(stream,4);
myFile->close();
QMessageBox::information(this,"save tip","xml文件保存成功!");
}
保存xml文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Begin>
<Type MyLover="yxx">
<sex>girl</sex>
<school>Xian</school>
<age>1996</age>
<special>beautiful</special>
</Type>
</Begin>
流方法读写XML
QXmlStreamReader方法读XML
关联QXmlStreamReader类与文件类,然后用QXmlStreamReader类来操作,但切记,未操作完不能close文件类,否则无法正确读取。该类根据标记读取,读取到不同的标记会返回不同的值,与DOM思想完全不一样,但是不支持随机读取。标记的划分是根据XML语法中的属性(值)文本,开始结束关键字进行的。贴出代码与运行效果。 代码:
void Widget::streamRead()
{
QString fileName = QFileDialog::getOpenFileName(this,"get xml file",
"C:/Users/Jack_Chen/Desktop","xml(*.xml)");
if(fileName.isEmpty()){
return;
}
myFile->setFileName(fileName);
bool ret =myFile->open(QIODevice::ReadOnly|QIODevice::Text);
if(!ret){
QMessageBox::warning(this,"warning","打开文件失败!");
return;
}
QXmlStreamReader readxml(myFile);
while(!readxml.atEnd()){
if(readxml.hasError()){
QMessageBox::warning(this,"warning","读取文件失败!");
break;
}
QXmlStreamReader::TokenType type = readxml.readNext();
switch ((int)type) {
case QXmlStreamReader::ProcessingInstruction:
qDebug()<<"ProcessingInstruction"<<readxml.text();
break;
case QXmlStreamReader::StartDocument:
qDebug()<<"StartDocument"<<"输出版本编码信息"<<readxml.documentVersion()<<readxml.documentEncoding();
break;
case QXmlStreamReader::StartElement:
{
qDebug()<<readxml.name().toString().append("!!!");
QString str = readxml.attributes().value("myLover").toString();
if(!str.isEmpty()){
qDebug()<<"输出属性值"<<readxml.name()<<str;
}
break;
}
case QXmlStreamReader::EndElement:
break;
case QXmlStreamReader::EndDocument:
qDebug()<<"EndDocument";
break;
case QXmlStreamReader::Characters:
{
QString ss = readxml.text().toString();
if(!readxml.isWhitespace()){
qDebug()<<"输出文本值"<<ss;
}
break;
}
}
}
}
打开的xml文件为本贴前文最上面部分的xml文件,效果如下:
QXmlStreamWriter方法写XML
流方法的思想除了数据还有标记的概念,通过标记区分位置,因此除了写数据之外还要写一些标记。跟读的思想完全一样,只不过具体的函数不太一样。直接贴代码
void Widget::streamWrite()
{
QString fileName = QFileDialog::getSaveFileName(this,"Save xml file",
"C:/Users/Jack_Chen/Desktop","xml(*.xml)");
if(fileName.isEmpty()){
return;
}
myFile->setFileName(fileName);
bool ret =myFile->open(QIODevice::WriteOnly|QIODevice::Text);
if(!ret){
QMessageBox::warning(this,"warning","保存文件失败!");
return;
}
QXmlStreamWriter stream(myFile);
stream.setAutoFormatting(true);
stream.writeStartDocument();
stream.writeStartElement("begin");
stream.writeStartElement("Type");
stream.writeAttribute("myLover","yxx");
stream.writeStartElement("sex");
stream.writeCharacters("girl");
stream.writeEndElement();
stream.writeStartElement("school");
stream.writeCharacters("Xian");
stream.writeEndElement();
stream.writeStartElement("age");
stream.writeCharacters("1996");
stream.writeEndElement();
stream.writeStartElement("special");
stream.writeCharacters("beautiful");
stream.writeEndElement();
stream.writeEndElement();
stream.writeEndElement();
stream.writeEndDocument();
myFile->close();
QMessageBox::information(this,"save tip","xml文件保存成功!");
}
写完打开文件得到xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<begin>
<Type myLover="yxx">
<sex>girl</sex>
<school>Xian</school>
<age>1996</age>
<special>beautiful</special>
</Type>
</begin>
|