XML是ExtensibleMarkup Language的缩写,即可扩展标记语言。它是一种用来创建的标记的标记语言。使用XML标记语言可以做到数据或数据结构在任何编程语言环境下的共享。XML中有格式正规的XML和有效的XML。
XML文档的元素一般是由标记头、标记末和标记间的字符串数据构成。元素可包含其他元素、文本或者两者的混合物。元素也可以拥有属性。一般格式: Aquotion。元素中可以插入属性,但是属性值一定要用双引号。
XML解析器是读取XML文档并提供对文档内容的访问的软件模块。在应用程序中,和其他系统中使用xml后,可以轻松实现数据交换。
A、QXmlStreamReader
:一种快速的基于流的方式访问良格式 XML 文档,特别适合于实现一次解析器(所谓“一次解析器”,可以理解成我们只需读取文档一次,然后像一个遍历器从头到尾一次性处理 XML 文档,期间不会有反复的情况,也就是不会读完第一个标签,然后读第二个,读完第二个又返回去读第一个,这是不允许的);这种方法是通过调用readNext()函数实现的,可以读取下一个记号,然后返回一个记号类型。这是一种快速解析
B、DOM(Document Object Model)
:将整个 XML 文档读入内存,构建成一个树结构,允许程序在树结构上向前向后移动导航,这是与另外两种方式最大的区别,也就是允许实现多次解析器(对应于前面所说的一次解析器)。DOM 方式带来的问题是需要一次性将整个 XML 文档读入内存,因此会占用很大内存;但是因为储存在内存里,所以实现频繁修改比较方便
A、QXmlStreamWriter
与QXmlStreamReader
相对应。XML流的方法
B、DOM
方式,首先在内存中生成 DOM
树,然后将 DOM
树写入文件。
项目配置
pro文件里面添加QT+=xml
include ,也可以include
void writeXml()
{
QString strDir = QCoreApplication::applicationDirPath() + "/xmlfile";
QDir dir(strDir);
if(!dir.exists(strDir))//先创建个文件夹
{
dir.cd(strDir);
dir.mkdir(strDir);
}
dir.setCurrent(strDir);
m_filePath = strDir + "/text.xml";
QFile file(m_filePath); //相对路径、绝对路径、资源路径都可以
if(!file.open(QFile::WriteOnly)) //可以用QIODevice,Truncate表示清空原来的内容
return;
QDomDocument doc;
QDomProcessingInstruction instruction; //添加处理命令
instruction = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
doc.appendChild(instruction);
//添加根节点
QDomElement root = doc.createElement("library");
doc.appendChild(root);
//添加第一个子节点及其子元素
QDomElement book = doc.createElement("book");
book.setAttribute("id",1); //方式一:创建属性 其中键值对的值可以是各种类型
QDomAttr time = doc.createAttribute("time"); //方式二:创建属性 值必须是字符串
time.setValue("2013/6/13");
book.setAttributeNode(time);
QDomElement title = doc.createElement("title"); //创建子元素
QDomText text; //设置括号标签中间的值
text = doc.createTextNode("C++ primer");
book.appendChild(title);
title.appendChild(text);
QDomElement author = doc.createElement("author"); //创建子元素
text = doc.createTextNode("Stanley Lippman");
author.appendChild(text);
book.appendChild(author);
root.appendChild(book);
//添加第二个子节点及其子元素,部分变量只需重新赋值
book = doc.createElement("book");
book.setAttribute("id",2);
time = doc.createAttribute("time");
time.setValue("2007/5/25");
book.setAttributeNode(time);
title = doc.createElement("title");
text = doc.createTextNode("Thinking in Java");
book.appendChild(title);
title.appendChild(text);
author = doc.createElement("author");
text = doc.createTextNode("Bruce Eckel");
author.appendChild(text);
book.appendChild(author);
root.appendChild(book);
//输出到文件
QTextStream out_stream(&file);
doc.save(out_stream,4); //缩进4格
file.close();
}
void readXml()
{
//打开或创建文件
QFile file(m_filePath); //相对路径、绝对路径、资源路径都行
if(!file.open(QFile::ReadOnly))
return;
QDomDocument doc;
if(!doc.setContent(&file))
{
file.close();
return;
}
file.close();
QDomElement root = doc.documentElement(); //返回根节点
qDebug() << root.nodeName();
QDomNode node = root.firstChild(); //获得第一个子节点
while(!node.isNull()) //如果节点不空
{
if(node.isElement()) //如果节点是元素
{
QDomElement e = node.toElement(); //转换为元素,注意元素和节点是两个数据结构,其实差不多
qDebug() << e.tagName() << " " << e.attribute("id") << " " << e.attribute("time"); //打印键值对,tagName和nodeName是一个东西
QDomNodeList list= e.childNodes();
for(int i = 0;i < list.count(); i++) //遍历子元素,count和size都可以用,可用于标签数计数
{
QDomNode n = list.at(i);
if(node.isElement())
qDebug()<
void addXml()
{
//打开文件
QFile file(m_filePath); //相对路径、绝对路径、资源路径都可以
if(!file.open(QFile::ReadOnly))
return;
//增加一个一级子节点以及元素
QDomDocument doc;
if(!doc.setContent(&file))
{
file.close();
return;
}
file.close();
QDomElement root = doc.documentElement();
QDomElement book = doc.createElement("book");
book.setAttribute("id",3);
book.setAttribute("time","1813/1/27");
QDomElement title = doc.createElement("title");
QDomText text;
text = doc.createTextNode("Pride and Prejudice");
title.appendChild(text);
book.appendChild(title);
QDomElement author = doc.createElement("author");
text = doc.createTextNode("Jane Austen");
author.appendChild(text);
book.appendChild(author);
root.appendChild(book);
if(!file.open(QFile::WriteOnly)) //先读进来,再重写,如果不用truncate就是在后面追加内容,就无效了
return;
//输出到文件
QTextStream out_stream(&file);
doc.save(out_stream,4); //缩进4格
file.close();
}
void removeXml()
{
//打开文件
QFile file(m_filePath); //相对路径、绝对路径、资源路径都可以
if(!file.open(QFile::ReadOnly))
return;
//删除一个一级子节点及其元素,外层节点删除内层节点于此相同
QDomDocument doc;
if(!doc.setContent(&file))
{
file.close();
return;
}
file.close(); //一定要记得关掉啊,不然无法完成操作
QDomElement root = doc.documentElement();
QDomNodeList list = doc.elementsByTagName("book"); //由标签名定位
for(int i = 0;i < list.count(); i++)
{
QDomElement e = list.at(i).toElement();
if(e.attribute("time")=="2007/5/25") //以属性名定位,类似于hash的方式,warning:这里仅仅删除一个节点,其实可以加个break
root.removeChild(list.at(i));
}
if(!file.open(QFile::WriteOnly|QFile::Truncate))
return;
//输出到文件
QTextStream out_stream(&file);
doc.save(out_stream,4); //缩进4格
file.close();
}
void updateXml()
{
//打开文件
QFile file(m_filePath); //相对路径、绝对路径、资源路径都可以
if(!file.open(QFile::ReadOnly))
return;
//更新一个标签项,如果知道xml的结构,直接定位到那个标签上定点更新
//或者用遍历的方法去匹配tagname或者attribut,value来更新
QDomDocument doc;
if(!doc.setContent(&file))
{
file.close();
return;
}
file.close();
QDomElement root = doc.documentElement();
QDomNodeList list = root.elementsByTagName("book");
QDomNode node = list.at(list.size()-1).firstChild(); //定位到第三个一级子节点的子元素
QDomNode oldnode = node.firstChild(); //标签之间的内容作为节点的子节点出现,当前是Pride and Projudice
node.firstChild().setNodeValue("Emma");
QDomNode newnode = node.firstChild();
node.replaceChild(newnode,oldnode);
if(!file.open(QFile::WriteOnly|QFile::Truncate))
return;
//输出到文件
QTextStream out_stream(&file);
doc.save(out_stream,4); //缩进4格
file.close();
}
对应常用api介绍:
writeStartDocument():写文档头,作用类似于创建一个xml文档,并在文档开头部分写入版本信息和编码信息默认为:
同时也可以写1个参数与2个参数.
writeEndDocument(): 对应于writeStartDocument(),当调用这个函数时,即表示文档信息写入完毕。
writeStartElement(): 写入开始记号 比如
writeEndElement(): 写入结束记号 比如
writeTextElement(): 写入文本信息 比如上面的name,phone,time 等等;
void xmlStreamWriter()
{
QString strDir = QCoreApplication::applicationDirPath() + "/xmlfile";
QDir dir(strDir);
if(!dir.exists(strDir))//先创建个文件夹
{
dir.cd(strDir);
dir.mkdir(strDir);
}
dir.setCurrent(strDir);
m_xmlPath = strDir + "/xmltest.xml";
QStringList str;
str << "通天" << "帝江" << "祖龙" << "红云";
QFile file(m_xmlPath);
if(!file.open(QFile::WriteOnly|QFile::Text))
{
qDebug()<< "file cannot open";
return;
}
QXmlStreamWriter stream(&file);
stream.setAutoFormatting(true); //
stream.writeStartDocument(); //默认:
stream.writeStartElement("DATA");
for (int i = 0; i < str.size(); i++)
{
stream.writeStartElement("EP");//
stream.writeTextElement("name", str[i]);
stream.writeTextElement("phone", "188XXXXXXXXXX");
stream.writeTextElement("time", "2022");
stream.writeEndElement();
}
stream.writeEndElement();
stream.writeEndDocument();
file.close();
}
文件内容:
通天
188XXXXXXXXXX
帝江
188XXXXXXXXXX
祖龙
188XXXXXXXXXX
红云
188XXXXXXXXXX
常用的api:
readNext(): 从xml输入流中读取下一个记号。
name(): 记号的名称,即<名称>名称>
isStartElement():判断当前已读取的记号是否为开始元素,开始元素即<>
isEndElement():判断当前已读取的记号是否为结束元素,结束元素即>
readElementText():读取当前记号对应的文本值,<>文本值>
atEnd():判断是否为文件结尾
void xmlStreamReader()
{
QString str;
QFile file(m_xmlPath);
if(file.open(QFile::ReadOnly|QFile::Text))
{
QXmlStreamReader reader(&file);
reader.readNext(); //过滤掉头一行
while(!reader.atEnd())
{
if(reader.isStartElement())
{
if(reader.name() == "EP")
{
str = "EP";
}
else if (reader.name() == "name")
{
str = reader.readElementText();
qDebug() << str << endl;
}
else if (reader.name() == "phone")
{
str = reader.readElementText();
qDebug() << str << endl;
}
else if(reader.name() == "time" )
{
str = reader.readElementText();
qDebug() << str << endl;
}
}
else if (reader.isEndElement())
{
if(reader.name() == "EP")
{
qDebug()<
输出结果:
"通天"
"188XXXXXXXXXX"
"2022"
"帝江"
"188XXXXXXXXXX"
"2022"
"祖龙"
"188XXXXXXXXXX"
"2022"
"红云"
"188XXXXXXXXXX"
"2022"