Qt之xml文件解析SAX

Qt之XML文件解析SAX

XML简介

​ 和HTML的语法很相似,但不同之处在于: HTML 被设计用来显示数据,其关注的是数据的外观,XML 被设计用来传输和存储数据,其关注的是数据的内容,因此,XML主要用来作为数据的存储和共享。

​ XML文档是一种树的结构,从根部扩展到枝叶。以下是一个XML示例


<root>
	
	<class name="Rect">
		<object name="obj1">
			<x1>10x1>
			<y1>10y1>
			<x2>50x2>
			<y1>50y1>
			<linewidth>2linewidth>
			<scale>0scale>
			<rotate>0rotate>
		object>
	class>
root>

其中第一行 是XML 声明。它定义 XML 的版本和所使用的编码格式, 为根节点的起始(在XML中可以自定义节点名称), 为子元素,其中name为其属性,值为Rect;每一个子元素都可以拥有子元素,故class的子元素为object,依次类推; 所有的元素都可以有文本内容和属性,如x1的文本为10,x2的文本为50。

写XML文件

​ 使用QT自带的模块QXmlStreamWriter

1. 构造函数

​ QXmlStreamWriter有三种构造方式,分别是

QXmlStreamWriter::QXmlStreamWriter([QString](qstring.html) **string*);
QXmlStreamWriter::QXmlStreamWriter([QByteArray](qbytearray.html) **array*);
QXmlStreamWriter::QXmlStreamWriter([QIODevice](qiodevice.html) **device*)

第一种和第二种分别是创建一个QString和QByteArray对象,然后将xml流写入到其中,第三种则是创建一个文件设备将xml流写入其中,一般来说,第三种常用。

2. 属性

autoFormatting:bool量,表示是否自动格式化文档,其相关读写函数为:

void setAutoFormatting(bool enable)
bool autoFormatting() const

autoFormattingIndent:int变量,当自动格式化为真时,它表示缩进的空格或者制表符的数量,相关读写函数为:

int autoFormattingIndent() const
void setAutoFormattingIndent(int spacesOrTabs)

一般4个空格等于一个制表符,也就是说setAutoFormattingIndent(4)的效果与setAutoFormattingIndent(-1)的效果是一样的。

3. 方法

​ 这里只介绍常用的一些方法。

1、自动格式化代码

void QXmlStreamWriter::setAutoFormatting(bool enable)

2、 设置文档编码

void QXmlStreamWriter::setCodec(QTextCodec *codec);
void QXmlStreamWriter::setCodec(const char *codecName)

​ 默认是utf-8,其它支持的编码格式如下:

Big5
Big5-HKSCS
CP949
EUC-JP
EUC-KR
GB18030
HP-ROMAN8
IBM 850
IBM 866
IBM 874
ISO 2022-JP
ISO 8859-1 to 10
ISO 8859-13 to 16
Iscii-Bng, Dev, Gjr, Knd, Mlm, Ori, Pnj, Tlg, and Tml
KOI8-R
KOI8-U
Macintosh
Shift-JIS
TIS-620
TSCII
UTF-8
UTF-16
UTF-16BE
UTF-16LE
UTF-32
UTF-32BE
UTF-32LE
Windows-1250 to 1258

3、设置自动缩进值

void setAutoFormattingIndent(int spacesOrTabs)

4、开始写xml文档时,需要先设置xml的版本和所使用的编码,一般为1.0版本,编码为utf-8,相关函数为:

void QXmlStreamWriter::writeStartDocument();
void QXmlStreamWriter::writeStartDocument(const QString &version);
void QXmlStreamWriter::writeStartDocument(const QString &version, bool standalone)

与其相对应的,关闭文档写入需要调用函数

void QXmlStreamWriter::writeEndDocument();

5、xml文档为树结构,其有一个根节点,根节点下面都挂在着许多元素,节点元素设置都为以下函数

void QXmlStreamWriter::writeStartElement(const QString &namespaceUri, const QString &name);
void QXmlStreamWriter::writeStartElement(const QString &qualifiedName)

与其相对应的,关闭节点或元素需要调用函数

void QXmlStreamWriter::writeEndElement()

6、每一个元素都可以有若干个属性,写入带有名称和值的属性

void QXmlStreamWriter::writeAttribute(const QString &namespaceUri, const QString &name, const QString &value);
void QXmlStreamWriter::writeAttribute(const QString &qualifiedName, const QString &value);
void QXmlStreamWriter::writeAttribute(const QXmlStreamAttribute &attribute);
void QXmlStreamWriter::writeAttributes(const QXmlStreamAttributes &attributes);

7、写文本

void QXmlStreamWriter::writeCharacters(const QString &text);

8、写入带有名称和文本的元素

void QXmlStreamWriter::writeTextElement(const QString &namespaceUri, const QString &name, const QString &text);
void QXmlStreamWriter::writeTextElement(const QString &qualifiedName, const QString &text);

9、写注释

void QXmlStreamWriter::writeComment(const QString &text);

还有一些方法就不一一介绍了,以上已经可以写出一个完整的xml文档。

4. 示例

​ 将一些图元的信息写入到xml文件中。

void MainWindow::on_btnWrite_clicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, "Save File",
                                                    "./untitled.xml","Xml(*.xml)");
    qDebug() << fileName;

    QFile file(fileName);

    if (!file.open(QIODevice::WriteOnly))
    {
        qDebug() << "Save failed";
        return;
    }

    /* 打开文件成功,开始写入 */
    QXmlStreamWriter writer(&file);

    writer.setCodec("utf-8");
    writer.writeStartDocument("1.0");
    writer.setAutoFormatting(true);
    writer.setAutoFormattingIndent(-1);

    writer.writeStartElement("root");
        writer.writeComment("写入矩形图元的信息");
        writer.writeStartElement("class");
        writer.writeAttribute("name", "Rect");
            writer.writeStartElement("object");
            writer.writeAttribute("name", "obj1");
                writer.writeTextElement("x1", QString::number(10));
                writer.writeTextElement("y1", QString::number(10));
                writer.writeTextElement("x2", QString::number(50));
                writer.writeTextElement("y1", QString::number(50));
                writer.writeTextElement("linewidth", QString::number(2));
                writer.writeTextElement("scale", QString::number(0));
                writer.writeTextElement("rotate", QString::number(0));
            writer.writeEndElement();

            writer.writeStartElement("object");
            writer.writeAttribute("name", "obj2");
                writer.writeTextElement("x1", QString::number(20));
                writer.writeTextElement("y1", QString::number(30));
                writer.writeTextElement("x2", QString::number(50));
                writer.writeTextElement("y1", QString::number(60));
                writer.writeTextElement("linewidth", QString::number(2));
                writer.writeTextElement("scale", QString::number(0));
                writer.writeTextElement("rotate", QString::number(90));

            writer.writeEndElement();
        writer.writeEndElement();

        writer.writeComment("写入线图元的信息");
        writer.writeStartElement("class");
        writer.writeAttribute("name", "Line");
            writer.writeStartElement("object");
            writer.writeAttribute("name", "obj1");
                writer.writeTextElement("x1", QString::number(10));
                writer.writeTextElement("y1", QString::number(10));
                writer.writeTextElement("x2", QString::number(50));
                writer.writeTextElement("y1", QString::number(50));
                writer.writeTextElement("linewidth", QString::number(2));
                writer.writeTextElement("scale", QString::number(0));
                writer.writeTextElement("rotate", QString::number(0));
            writer.writeEndElement();

            writer.writeStartElement("object");
            writer.writeAttribute("name", "obj2");
                writer.writeTextElement("x1", QString::number(10));
                writer.writeTextElement("y1", QString::number(10));
                writer.writeTextElement("x2", QString::number(50));
                writer.writeTextElement("y1", QString::number(50));
                writer.writeTextElement("linewidth", QString::number(2));
                writer.writeTextElement("scale", QString::number(0));
                writer.writeTextElement("rotate", QString::number(0));
            writer.writeEndElement();
        writer.writeEndElement();

        writer.writeComment("写入椭圆图元信息");
        writer.writeStartElement("class");
        writer.writeAttribute("name", "Ellipse");
        writer.writeCharacters("无椭圆图元");
        writer.writeEndElement();
    writer.writeEndElement();
    writer.writeEndDocument();
    file.close();
}

最后构成的xml文档如下:


<root>
	
	<class name="Rect">
		<object name="obj1">
			<x1>10x1>
			<y1>10y1>
			<x2>50x2>
			<y1>50y1>
			<linewidth>2linewidth>
			<scale>0scale>
			<rotate>0rotate>
		object>
		<object name="obj2">
			<x1>20x1>
			<y1>30y1>
			<x2>50x2>
			<y1>60y1>
			<linewidth>2linewidth>
			<scale>0scale>
			<rotate>90rotate>
		object>
	class>
	
	<class name="Line">
		<object name="obj1">
			<x1>10x1>
			<y1>10y1>
			<x2>50x2>
			<y1>50y1>
			<linewidth>2linewidth>
			<scale>0scale>
			<rotate>0rotate>
		object>
		<object name="obj2">
			<x1>10x1>
			<y1>10y1>
			<x2>50x2>
			<y1>50y1>
			<linewidth>2linewidth>
			<scale>0scale>
			<rotate>0rotate>
		object>
	class>
	
	<class name="Ellipse">无椭圆图元class>
root>

读XML文件

​ 使用QT自带的模块QXmlStreamReader

1. 构造函数

可以通过

QXmlStreamReader(const char *data)
QXmlStreamReader(const QString &data)
QXmlStreamReader(const QByteArray &data)
QXmlStreamReader(QIODevice *device)
QXmlStreamReader()

2. 属性

namespaceProcessing:bool变量,指是否处理命名空间,相关读写函数为

bool namespaceProcessing() const
void setNamespaceProcessing(bool)

3. 方法

1、设置文件设备,如果你使用了无参的构造,则可以通过下面这个函数来设置要读取的文件

void setDevice(QIODevice *device)

2、读取下一个下一个标记

QXmlStreamReader::TokenType QXmlStreamReader::readNext();

读取xml文档主要就用这一个函数,通过其返回值 QXmlStreamReader::TokenType来判断读取的是什么元素,枚举TokenType如下

    enum TokenType {
        NoToken = 0,
        Invalid,
        StartDocument,
        EndDocument,
        StartElement,
        EndElement,
        Characters,
        Comment,
        DTD,
        EntityReference,
        ProcessingInstruction
    };

  • NoToken:没有读取到任何信息
  • Invalid:读取失败,错误信息保存在error()errorString()
  • StartDocument:读取到的为xml文档版本号documentVersion()和编码格式documentEncoding()
  • EndDocument:读取到文档结束的位置
  • StartElement:读取到的为一个节点,节点命名空间和名字分别保存在namespaceUri()name()中,其属性值保存在attribute()中。
  • EndElement:读取到该节点的结束位置
  • Characters:读取的内容为字符,保存在text()
  • Comment:读取的内容为注释,保存在text()
  • DTD:读取的内容为DTD,保存在text()
  • EntityReference:无法解析的实体引用
  • ProcessingInstruction

3、读取开始节点中的文本

QString QXmlStreamReader::readElementText(QXmlStreamReader::ReadElementTextBehaviour behaviour = ErrorOnUnexpectedElement)

这个函数用在当读取的标记为StartElement之后,直接调用这个函数可以更方便的获取其内容

示例

​ 读取上面写入的文档,示例代码为:

void MainWindow::on_btnRead_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Save this file As"
                                                    , "./", "Xml(*.xml)");
    qDebug() << fileName;

    QFile file(fileName);

    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug() << "Save failed";
        return;
    }

    QXmlStreamReader reader(&file);
    QXmlStreamReader::TokenType token;
    QXmlStreamAttributes attribute;
    QString str, attrStr;
    while (!reader.atEnd()) {
          token = reader.readNext();
          if(token == QXmlStreamReader::StartElement)
          {
              str =  reader.name().toString();
              if(QString::compare(str, "root") == 0)
              {
                  qDebug() << "这是根节点:"<< str;
                  qDebug() << "----------------";
              }
              else if(QString::compare(str, "class") == 0)
              {

                  attribute = reader.attributes();
                  if(attribute.hasAttribute("name"))
                  {
                      attrStr.clear();
                      attrStr = attribute.value("name").toString();
                  }
                  qDebug() << "************************************************";
                  qDebug() << "这是元素:"<< str << ", 属性为" << attrStr;
              }
              else if(!QString::compare(str, "object"))
              {
                  attribute = reader.attributes();
                  if(attribute.hasAttribute("name"))
                  {
                      attrStr.clear();
                      attrStr = attribute.value("name").toString();
                  }
                  qDebug() << "这是元素:" << str << ", 属性为" << attrStr;

              }
              else if(QString::compare(str, "x1") == 0)
              {

                  qDebug() << attrStr << "对象的x1值为:" << reader.readElementText();
              }
              else if(QString::compare(str, "x2") == 0)
              {
                  qDebug() << attrStr << "对象的x2值为:" << reader.readElementText();
              }
              else if(QString::compare(str, "y1") == 0)
              {
                  qDebug() << attrStr << "对象的y1值为:" << reader.readElementText();
              }
              else if(QString::compare(str, "y2") == 0)
              {
                  qDebug() << attrStr << "对象的y2值为:" << reader.readElementText();
              }
              else if(QString::compare(str, "linewidth") == 0)
              {
                  qDebug() << attrStr << "对象的linewidth值为:" << reader.readElementText();
              }
              else if(QString::compare(str, "scale") == 0)
              {
                  qDebug() << attrStr << "对象的scale值为:" << reader.readElementText();
              }
              else if(QString::compare(str, "rotate") == 0)
              {
                  qDebug() << attrStr << "对象的rotate值为:" << reader.readElementText();
                  qDebug() << "----------------";
              }
          }
          else if(token == QXmlStreamReader::Characters)
          {
//              qDebug() << reader.text().toString(); /* 会把换行符和制表符也给读出来 */
          }
          else if(token == QXmlStreamReader::Comment)
          {
              qDebug() << reader.text().toString(); /* xml注释 */
          }
          else if(token == QXmlStreamReader::Invalid)
          {
              qDebug() << "读取失败:" << reader.errorString();
          }
    }
}

打印输出结果如下:

"H:/robot_plus/tests/Xml/XML/bin/untitled1.xml"
这是根节点: "root"
----------------
"写入矩形图元的信息"
************************************************
这是元素: "class" , 属性为 "Rect"
这是元素: "object" , 属性为 "obj1"
"obj1" 对象的x1值为: "10"
"obj1" 对象的y1值为: "10"
"obj1" 对象的x2值为: "50"
"obj1" 对象的y1值为: "50"
"obj1" 对象的linewidth值为: "2"
"obj1" 对象的scale值为: "0"
"obj1" 对象的rotate值为: "0"
----------------
这是元素: "object" , 属性为 "obj2"
"obj2" 对象的x1值为: "20"
"obj2" 对象的y1值为: "30"
"obj2" 对象的x2值为: "50"
"obj2" 对象的y1值为: "60"
"obj2" 对象的linewidth值为: "2"
"obj2" 对象的scale值为: "0"
"obj2" 对象的rotate值为: "90"
----------------
"写入线图元的信息"
************************************************
这是元素: "class" , 属性为 "Line"
这是元素: "object" , 属性为 "obj1"
"obj1" 对象的x1值为: "10"
"obj1" 对象的y1值为: "10"
"obj1" 对象的x2值为: "50"
"obj1" 对象的y1值为: "50"
"obj1" 对象的linewidth值为: "2"
"obj1" 对象的scale值为: "0"
"obj1" 对象的rotate值为: "0"
----------------
这是元素: "object" , 属性为 "obj2"
"obj2" 对象的x1值为: "10"
"obj2" 对象的y1值为: "10"
"obj2" 对象的x2值为: "50"
"obj2" 对象的y1值为: "50"
"obj2" 对象的linewidth值为: "2"
"obj2" 对象的scale值为: "0"
"obj2" 对象的rotate值为: "0"
----------------
"写入椭圆图元信息"
************************************************
这是元素: "class" , 属性为 "Ellipse"

总结

​ 使用SAX方式进行读写XML文档更易上手,也更符合XML标准,不足之处在于仅支持迭代读写,如果你想要更改某个属性,只能重新开始写入。使用DOM方式进行解析则可以达到增删改查的操作。但是两者各有利弊。

你可能感兴趣的:(QT,qt,xml,开发语言)