在项目开发中,配置文件通常分为三种:ini文件、xml文件和json文件,个人认为三种文件的区别在于:ini文件记录方便、格式简单但不便于扩展;xml文件和json文件扩展性强,能够记录更复杂的配置文件,但格式相对复杂,特别是**对重复项的记录有优势**。因此,在选择配置文件时,如文件内容较少,无(少)重复记录项,可选择ini文件,若文件内容多、逻辑层次复杂、需要重复记录多组数据或者后期后期可能需要扩展多层关系,可选择xml或json文件。

1.INI文件

Qt通过QSettings类读写ini文件(但是QSetting还可以读取其它类型的配置文件,例如:注册表)头文件:QSetting.h,QSetting读写ini文件的步骤为:
    * 通过路径名称打开文件并设定为ini文件格式
    * 读/写数据
    * 关闭文件,删除句柄

Qt读文件示例如下:

//打开文件并指定为ini格式
QSettings* configIni = new QSettings(file_path, QSettings::IniFormat);
QString qTemp = "";
//读指定节点的指定数据,其中“Log”为节点,save_days为具体数据项
logDays = configIni->value("Log/save_days").toInt();
qTemp = configIni->value("Log/print_debug").toString();
printDbgLog = qTemp.compare("true", Qt::CaseInsensitive) == 0 ? true : false;
//删除指针,此处未关联父对象,必须手动删除,否则有内存泄露风险
delete configIni;

Qt写文件示例如下:

//打开文件
QSettings* configIni = new QSettings(filePath, QSettings::IniFormat);
QString qTemp = "";
//写入数据,必须指定写入节点和值
configIni->setValue("Public/remove_time", removeTime);     //定时任务执行时间
configIni->setValue("Log/save_days", logDays);             //日志保存天数
      configIni->setValue("Log/print_debug", "true");
else
      configIni->setValue("Log/print_debug", "false");
delete  configIni;

2.XML文件

Qt有多种方法读取xml文件,有人在网上总结了几种方式,具体看这里,我使用的是DOM的方式,这种方式的有点在于理解简单,读写完全按照xml文件的层级操作即可;缺点则是需要将文件完全放入内存后才可读写,也就是说,对于非常大的xml文件,这不是一种理想的处理方式。使用DOM方式解析xml必须包含头文件:<QtXml/qxml.h>和<QtXml/QDomComment>

DOM方式读取xml文件的过程如下:

  a.读取文件内容并整体转换成DOM结构树存储于内存中;

  b.按结构解析节点数据;

  c.清理内存

  具体示例代码:

//xml类型配置文件读写
bool ConfigFile::LoadXMLFile(QString file_path)
{
    bool bRet = true;
    //step1:读文件
    QFile file(file_path);
    if (!file.open(QFile::ReadOnly))
    {
        errMsg = QString("文件打开失败,%1").arg(file.errorString());
        return false;
    }

    QString errorStr;
    int errorLine;
    int errorColumn;

    //qml数据存储格式
    QDomDocument doc;
    //step2:转换成xml数据格式
    if (!doc.setContent(&file, false, &errorStr, &errorLine, &errorColumn))
    {
        errMsg = QString("QML解析错误,%1,%2,%3").arg(errorStr)
             .arg(QString::number(errorLine)).arg(QString::number(errorColumn));
         file.close();
         return false;
    }
    //此时已经不需要文件句柄,可关闭
    file.close();

    //根节点元素
    QDomElement root = doc.documentElement();

 #pragma region "每个文件不一样,由此处开始修改"
     //简单节点结构
     QDomElement parts = root.firstChildElement("module");
     if (parts.isNull())
     {
         bRet = false;
         errMsg = "文件格式异常,未发现[module]节点";
         goto _RET;
      }
     else
     {
         //ups
         QDomElement temp = parts.firstChildElement("ups");
         if (temp.isNull())
          {
              bRet = false;
              errMsg = "文件格式异常,未发现[model/ups]节点";
              goto _RET;
          }
          else
          {
              QString st = temp.text();
              if (st.compare("true") == 0)
                  modeUPS = true;
              else
                  modeUPS = false;
          }


      //多重嵌套、重复结构
      {
         parts = root.firstChildElement("processor_monitor");
         if (parts.isNull())
         {
             bRet = false;
             errMsg = QString("文件格式异常,未发现[processor_monitor]节点");
             goto _RET;
         }
         else
         {
               //循环解析,获取测量点数据
              QDomElement subElement = parts.firstChildElement();
              while (!subElement.isNull())
              {
                  if (subElement.tagName().compare("processor") == 0)
                  {
                     ProcessorInfo tempValue;
                      if (!GetProcessorInfo(subElement, tempValue))
                      {
                          bRet = false;
                          errMsg = QString("进程监控节点解析失败,%1").arg(errMsg);
                          goto _RET;
                      }
                      else
                          vecProcessorInfo.push_back(tempValue);
                  }

                  subElement = subElement.nextSiblingElement();
              }
          }
      }

 #pragma endregion

  _RET:
      doc.clear();
      return bRet;
}

View Code

DOM方式写入xml文件的过程如下:

  a.创建DOM结构树根节点

  b.在根节点上按层级插入数据节点

  c.将内存中的DOM数据结构转化为符合xml格式的字符串

  d.将字符串写入文件

  具体示例代码:

bool ConfigFile::SaveXMLFile()
{
    bool bRet = true;
    QFile file(filePath);
    QDomDocument doc;           //xml结构

    //以只写入方式打开,并且打开时清空原来内容
    if (!file.open(QFile::WriteOnly | QFile::Truncate))
    {
        errMsg = QString("文件打开失败,%1").arg(file.errorString());
        return false;
    }

    //写入xml头部
    QDomProcessingInstruction instruction; //添加处理命令
    instruction = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");
    doc.appendChild(instruction);

    //添加根节点
    QDomElement root = doc.createElement("root");
    doc.appendChild(root);

#pragma region  跟随配置文件记录项改变
    QString strTemp = "";
    QDomText text;

    //写入model节点
    {
        //ups
        QDomElement model = doc.createElement("module");
        QDomElement subNode = doc.createElement("ups");
        if (modeUPS)
            strTemp = "true";
        else
            strTemp = "false";
        text = doc.createTextNode(strTemp);        //设置括号标签中间的值
        subNode.appendChild(text);
        model.appendChild(subNode);

        //process
        subNode = doc.createElement("process");
        if (modeUPS)
            strTemp = "true";
        else
            strTemp = "false";
        text = doc.createTextNode(strTemp);        //设置括号标签中间的值
        subNode.appendChild(text);
        model.appendChild(subNode);
        root.appendChild(model);
    }


    //写入Processor_monitor节点数据
    {
        QDomElement eProcessorList = doc.createElement("processor_monitor");

        for (auto it : vecProcessorInfo)
        {
            QDomElement eProcessor = doc.createElement("processor");

            //name
            QDomElement eSub = doc.createElement("name");
            text = doc.createTextNode(it.processorName);        //设置括号标签中间的值
            eSub.appendChild(text);
            eProcessor.appendChild(eSub);

            //path
            eSub = doc.createElement("path");
            text = doc.createTextNode(it.processroPath);        //设置括号标签中间的值
            eSub.appendChild(text);
            eProcessor.appendChild(eSub);

            //autoStart
            if (it.isRestart)
                strTemp = "true";
            else
                strTemp = "false";
            eSub = doc.createElement("autostart");
            text = doc.createTextNode(strTemp);        //设置括号标签中间的值
            eSub.appendChild(text);
            eProcessor.appendChild(eSub);

            //property
            strTemp = QString::number(it.stopProprity);
            eSub = doc.createElement("proprity");
            text = doc.createTextNode(strTemp);        //设置括号标签中间的值
            eSub.appendChild(text);
            eProcessor.appendChild(eSub);

            eProcessorList.appendChild(eProcessor);
        }

        root.appendChild(eProcessorList);
    }

#pragma endregion

    //输出到文件
    QTextStream out_stream(&file);
    doc.save(out_stream, 4); //缩进4格
    file.close();
    doc.clear()
    return true;
}

View Code

DOM方式处理xml文件比较好理解:按照各个节点的实际从属关系插入/读取即可,但是对内存的使用效率偏低。

3.JSON文件

  在我的项目经验中,并没有碰到过用json文件作为配置文件的情况,个人理解,json多用于不同程序间的数据交互,相较与xml,它的冗余信息更少,但又不会丢失数据间逻辑关系,同时便于扩展,因此,个人认为,json数据更适合作为通信数据的载体而不是配置文件。

  Qt处理JSON数据的类为QJsonObject、QJsonDocument、QJsonValue、QJsonParseError,头文件与类名相同。JSON文件的处理与XML文件类似,读取文件的过程为:a.从文件读取数据;b.将字符型数据转换为JSON格式数据并存在内存;c.从内存结构中读取数据。写文件的过程为:a.创建内存结构;b.插入数据;c.将数据转化为字符型数据并存储到文本中。

以下代码展示了创建简单json数据的过程,最终结果QstrJson为QByteArray结构:

QJsonObject json;
json.insert("CMD", QString("GRM"));
json.insert("RM", macro_addr);

QJsonDocument document;
document.setObject(json);
QstrJson = document.toJson(QJsonDocument::Compact);
if (G_INIFILE->printDbgLog)
    LOG(INFO) << "发包数据:" << QstrJson.toStdString();

以下代码展示解析简单json的过程,其中,原始数据保存在qstrRecv中

//解析数据
    QJsonParseError parseJsonErr;
    QJsonDocument document = QJsonDocument::fromJson(qstrRecv.toUtf8(), &parseJsonErr);
    if (!(parseJsonErr.error == QJsonParseError::NoError))
    {
        errMsg = QString("解析json文件错误,%1").arg(parseJsonErr.errorString());
        return false;
    }

    QJsonObject jsonObject = document.object();

    //CMD
    QString temp = jsonObject["CMD"].toString();
    if (temp.compare("GRM") != 0)
    {
        errMsg = "收包命令错误";
        return false;
    }

    //RM
    int addrIn = jsonObject["RM"].toInt();
    if (addrIn != macro_addr)
    {
        errMsg = "收包Macro地址错误";
        return false;
    }

    //RET
    if (jsonObject.contains("RET"))
    {
        QJsonValue jsonValueList = jsonObject.value("RET");
        QJsonObject item = jsonValueList.toObject();

        //ERR
        int errCode = item["ERR"].toInt();
        if (errCode != 0)
        {
            //MSG
            QString err = item["MSG"].toString();
            errMsg = QString("CNC反馈错误,错误码:%1,详情:%2").arg(errCode).arg(err);
            return false;
        }
    }

    //DATA
    if (jsonObject.contains("DATA"))
    {
        QJsonValue jsonValueList = jsonObject.value("DATA");
        QJsonObject item = jsonValueList.toObject();
        macro_value = item["RMV"].toDouble();
    }

View Code

4.总结

为了以后少造轮子,根据自己的使用经验,我自己定义了一个配置文件读写类,可以读写ini文件和xml文件,具体如下:

#pragma once
#include <qsettings.h>
#include <qstring.h>
#include <qdir.h>
#include "qcoreapplication.h"
#include <mutex>
#include "DataDefine.h"
#include "vector"
#include <QtXml/qxml.h>
#include <QtXml/QDomDocument>

class CLock
{
private:
    std::mutex mux;
public:
    CLock() {}

    ~CLock() {}

    void Lock()
    {
        mux.lock();
    }

    void Unlock()
    {
        mux.unlock();
    }
};

class ConfigFile
{
public:
    ~ConfigFile();

private://禁用赋值初始化
    ConfigFile();
    ConfigFile(const ConfigFile&);
    ConfigFile& operator=(const ConfigFile&);

    static std::shared_ptr<ConfigFile> m_pInstance;
    static CLock m_lock;
public:
    //错误信息
    QString errMsg;
    //日志存储天数(天)
    int logDays;
    //是否打印调试日志
    bool printDbgLog;
    //定时任务执行时间
    QString removeTime;

public://UPS相关配置
    bool modeUPS;            //是否启用UPS模块
    bool modeProcess;        //是否启用process模块
    QString supplier;        //供应商
    QString model;            //型号
    QString serialName;        //com口
    int shutdownPCDelay;    //关闭电脑延时时间
    //进程监控相关配置
    std::vector<ProcessorInfo> vecProcessorInfo;

private:
    //文件类别
    int fileType;    
    //文件路径
    QString filePath;    
    //当前exe运行路径
    //QString iniPath;
    //exe目录
    QString exeDir;
public:
    //获取句柄,懒汉单例模式,双重锁,线程安全
    static std::shared_ptr<ConfigFile> getInstance()
    {
        if (nullptr == m_pInstance)
        {
            m_lock.Lock();
            if (nullptr == m_pInstance)
            {
                m_pInstance = std::shared_ptr<ConfigFile>(new ConfigFile);
            }
            m_lock.Unlock();
        }

        return m_pInstance;
    }

    //文件类别
    enum
    {
        INI_FILE = 0,        //.ini的配置文件
        XML_FILE            //.xml的配置文件
    };

    //读取配置文件
    bool LoadConfigFile(QString file_path, int file_type);
    //写入配置文件
    bool SaveConfigFile();

    //获取exe所在目录
    QString GetExeDir(void);
private:
    //ini文件读写操作
    bool LoadIniFile(QString file_path);
    bool SaveIniFile();

    //xml文件读写操作
    bool LoadXMLFile(QString file_path);
    bool SaveXMLFile();

public://按实际配置文件写
    //获取供应商
    QString GetSupplier();
    //获取UPS型号
    QString GetUPSModel();
    //获取串口名称
    QString GetSerialName();
    //获取延时时间
    int GetDelayMin();

private:
    //读取一个进程的参数
    bool GetProcessorInfo(const QDomElement &src,ProcessorInfo &value);
};

#define G_CONFIG ConfigFile::getInstance()

ConfigFile.h

View Code

#include "ConfigFile.h"

# pragma execution_character_set("utf-8")

std::shared_ptr<ConfigFile> ConfigFile::m_pInstance = nullptr;
CLock ConfigFile::m_lock;

ConfigFile::ConfigFile()
{
    //日志存储天数
    logDays = 7;
    //是否打印调试日志
    printDbgLog = false;

    exeDir = QCoreApplication::applicationDirPath();

    fileType = XML_FILE;
}

ConfigFile::ConfigFile(const ConfigFile&)
{

}

ConfigFile::~ConfigFile()
{

}

//读取配置文件
bool ConfigFile::LoadConfigFile(QString file_path, int file_type)
{
    filePath = file_path;
    fileType = file_type;

    if (file_type == INI_FILE)
        return LoadIniFile(file_path);
    else if (file_type == XML_FILE)
        return LoadXMLFile(file_path);
    else
    {
        errMsg = "文件类型设置错误";
        return false;
    }

    return false;
}

//写入配置文件
bool ConfigFile::SaveConfigFile()
{
    if (fileType == XML_FILE)
        return SaveXMLFile();
    else
        return SaveIniFile();
   
    return false;
}

//ini类型配置文件读写
bool ConfigFile::LoadIniFile(QString file_path)
{
    filePath = file_path;

    QSettings* configIni = new QSettings(file_path, QSettings::IniFormat);
    QString qTemp = "";
    //log
    logDays = configIni->value("Log/save_days").toInt();
    qTemp = configIni->value("Log/print_debug").toString();
    printDbgLog = qTemp.compare("true", Qt::CaseInsensitive) == 0 ? true : false;

    //定时任务时间
    removeTime = configIni->value("Public/remove_time").toString();

    //UPS参数
    {
        //供应商
        supplier = configIni->value("UPS/supplier").toString();

        //型号
        model = configIni->value("UPS/model").toString();

        //com口
        serialName = configIni->value("UPS/com").toString();

        //关闭电脑延时时间
        shutdownPCDelay = configIni->value("UPS/shutdown_pc_delay").toInt();
    }

    //进程参数
    {

    }
   
    delete configIni;

    return true;
}

bool ConfigFile::SaveIniFile()
{
    QSettings* configIni = new QSettings(filePath, QSettings::IniFormat);
    QString qTemp = "";

    //Public
    configIni->setValue("Public/remove_time", removeTime);     //定时任务执行时间

    //Log
    configIni->setValue("Log/save_days", logDays);             //日志保存天数
    
    if (printDbgLog)                                           //打印调试日志
        configIni->setValue("Log/print_debug", "true");
    else
        configIni->setValue("Log/print_debug", "false");

    /*
    //KValue
    configIni->setValue("KValue/part_name", qmlK.kPartName);              //零件名
    configIni->setValue("KValue/prg_num", qmlK.kCurPrgNum);               //程序编号
    configIni->setValue("KValue/param_name", qmlK.kParamName);            //被测参数名称
    configIni->setValue("KValue/standard_value", qmlK.kStandardValue);    //名义值
    configIni->setValue("KValue/lower_deviation", qmlK.kLowerDeviation);  //下公差
    configIni->setValue("KValue/upper_deviation", qmlK.kUpperDeviation);  //上公差
    configIni->setValue("KValue/measure_value", qmlK.kMeasureValue);      //测量值
    configIni->setValue("KValue/measure_time", qmlK.kMeasureTime);        //测量时间
    configIni->setValue("KValue/work_station", qmlK.kWorkStation);        //治具
    
    //CNC
    configIni->setValue("CNC/ip", cncParam.ip);                      //CNC IP地址
    configIni->setValue("CNC/port", cncParam.port);                  //CNC IP地址
    configIni->setValue("CNC/left_x1", cncParam.leftMain.x);         //左治具主孔X轴
    configIni->setValue("CNC/left_y1", cncParam.leftMain.y);         //左治具主孔Y轴
    configIni->setValue("CNC/left_z1", cncParam.leftMain.z);        //左治具主孔Z轴
    configIni->setValue("CNC/left_x2", cncParam.leftSub.x);          //左治具副孔X轴
    configIni->setValue("CNC/left_y2", cncParam.leftSub.y);          //左治具副孔Y轴
    configIni->setValue("CNC/left_z2", cncParam.leftSub.z);          //左治具副孔Z轴
    configIni->setValue("CNC/left_x3", cncParam.leftThird.x);        //左治具温水孔X轴
    configIni->setValue("CNC/left_y3", cncParam.leftThird.y);        //左治具温水孔Y轴
    configIni->setValue("CNC/left_z3", cncParam.leftThird.z);        //左治具温水孔Z轴
    configIni->setValue("CNC/right_x1", cncParam.rightMain.x);       //右治具主孔X轴
    configIni->setValue("CNC/right_y1", cncParam.rightMain.y);       //右治具主孔Y轴
    configIni->setValue("CNC/right_z1", cncParam.rightMain.z);       //右治具主孔Z轴
    configIni->setValue("CNC/right_x2", cncParam.rightSub.x);        //右治具副孔X轴
    configIni->setValue("CNC/right_y2", cncParam.rightSub.y);        //右治具副孔Y轴
    configIni->setValue("CNC/right_z2", cncParam.rightSub.z);        //右治具副孔Z轴
    configIni->setValue("CNC/right_x3", cncParam.rightThird.x);      //右治具温水孔X轴
    configIni->setValue("CNC/right_y3", cncParam.rightThird.y);      //右治具温水孔Y轴
    configIni->setValue("CNC/right_z3", cncParam.rightThird.z);      //右治具温水孔Z轴

    //Folder
    configIni->setValue("Folder/qml_src", qstrQMLSrc);      //QML源文件地址
    configIni->setValue("Folder/qml_dst", qstrQMLDst);      //QML目标文件地址

    //section_main
    configIni->setValue("Section_main/alarm_lower_limit_X", limit[0].alarmLowerLimit);
    configIni->setValue("Section_main/alarm_upper_limit_X", limit[0].alarmUpperLimit);
    configIni->setValue("Section_main/avoid_lower_limit_X", limit[0].avoidLowerLimit);
    configIni->setValue("Section_main/avoid_upper_limit_X", limit[0].avoidUpperLimit);

    configIni->setValue("Section_main/alarm_lower_limit_Y", limit[1].alarmLowerLimit);
    configIni->setValue("Section_main/alarm_upper_limit_Y", limit[1].alarmUpperLimit);
    configIni->setValue("Section_main/avoid_lower_limit_Y", limit[1].avoidLowerLimit);
    configIni->setValue("Section_main/avoid_upper_limit_Y", limit[1].avoidUpperLimit);

    configIni->setValue("Section_main/alarm_lower_limit_Z", limit[2].alarmLowerLimit);
    configIni->setValue("Section_main/alarm_upper_limit_Z", limit[2].alarmUpperLimit);
    configIni->setValue("Section_main/avoid_lower_limit_Z", limit[2].avoidLowerLimit);
    configIni->setValue("Section_main/avoid_upper_limit_Z", limit[2].avoidUpperLimit);

    //Section_sub
    configIni->setValue("Section_sub/alarm_lower_limit_X", limit[3].alarmLowerLimit);
    configIni->setValue("Section_sub/alarm_upper_limit_X", limit[3].alarmUpperLimit);
    configIni->setValue("Section_sub/avoid_lower_limit_X", limit[3].avoidLowerLimit);
    configIni->setValue("Section_sub/avoid_upper_limit_X", limit[3].avoidUpperLimit);

    configIni->setValue("Section_sub/alarm_lower_limit_Y", limit[4].alarmLowerLimit);
    configIni->setValue("Section_sub/alarm_upper_limit_Y", limit[4].alarmUpperLimit);
    configIni->setValue("Section_sub/avoid_lower_limit_Y", limit[4].avoidLowerLimit);
    configIni->setValue("Section_sub/avoid_upper_limit_Y", limit[4].avoidUpperLimit);

    configIni->setValue("Section_sub/alarm_lower_limit_Z", limit[5].alarmLowerLimit);
    configIni->setValue("Section_sub/alarm_upper_limit_Z", limit[5].alarmUpperLimit);
    configIni->setValue("Section_sub/avoid_lower_limit_Z", limit[5].avoidLowerLimit);
    configIni->setValue("Section_sub/avoid_upper_limit_Z", limit[5].avoidUpperLimit);

    //Section_third
    configIni->setValue("Section_third/alarm_lower_limit_X", limit[6].alarmLowerLimit);
    configIni->setValue("Section_third/alarm_upper_limit_X", limit[6].alarmUpperLimit);
    configIni->setValue("Section_third/avoid_lower_limit_X", limit[6].avoidLowerLimit);
    configIni->setValue("Section_third/avoid_upper_limit_X", limit[6].avoidUpperLimit);

    configIni->setValue("Section_third/alarm_lower_limit_Y", limit[7].alarmLowerLimit);
    configIni->setValue("Section_third/alarm_upper_limit_Y", limit[7].alarmUpperLimit);
    configIni->setValue("Section_third/avoid_lower_limit_Y", limit[7].avoidLowerLimit);
    configIni->setValue("Section_third/avoid_upper_limit_Y", limit[7].avoidUpperLimit);

    configIni->setValue("Section_third/alarm_lower_limit_Z", limit[8].alarmLowerLimit);
    configIni->setValue("Section_third/alarm_upper_limit_Z", limit[8].alarmUpperLimit);
    configIni->setValue("Section_third/avoid_lower_limit_Z", limit[8].avoidLowerLimit);
    configIni->setValue("Section_third/avoid_upper_limit_Z", limit[8].avoidUpperLimit);
    */

    delete  configIni;
    
    return true;
}

//xml类型配置文件读写
bool ConfigFile::LoadXMLFile(QString file_path)
{
    bool bRet = true;
    //step1:读文件
    QFile file(file_path);
    if (!file.open(QFile::ReadOnly))
    {
        errMsg = QString("文件打开失败,%1").arg(file.errorString());
        return false;
    }

    QString errorStr;
    int errorLine;
    int errorColumn;

    //qml数据存储格式
    QDomDocument doc;
    //step2:转换成xml数据格式
    if (!doc.setContent(&file, false, &errorStr, &errorLine, &errorColumn))
    {
        errMsg = QString("QML解析错误,%1,%2,%3").arg(errorStr)
            .arg(QString::number(errorLine)).arg(QString::number(errorColumn));
        file.close();
        return false;
    }
    file.close();

    //根节点元素    
    QDomElement root = doc.documentElement();

#pragma region "每个文件不一样,由此处开始修改"
    //mode
    QDomElement parts = root.firstChildElement("module");
    if (parts.isNull())
    {
        bRet = false;
        errMsg = "文件格式异常,未发现[module]节点";
        goto _RET;
    }
    else
    {
        //ups
        QDomElement temp = parts.firstChildElement("ups");
        if (temp.isNull())
        {
            bRet = false;
            errMsg = "文件格式异常,未发现[model/ups]节点";
            goto _RET;
        }
        else
        {
            QString st = temp.text();
            if (st.compare("true") == 0)
                modeUPS = true;
            else
                modeUPS = false;
        }
           
        //process
        temp = parts.firstChildElement("process");
        if (temp.isNull())
        {
            bRet = false;
            errMsg = "文件格式异常,未发现[model/process]节点";
            goto _RET;
        }
        else
        {
            QString st = temp.text();
            if (st.compare("true") == 0)
                modeProcess = true;
            else
                modeProcess = false;
        }
    }

    //log节点
    parts = root.firstChildElement("log");
    if (parts.isNull())
    {
        bRet = false;
        errMsg = "文件格式异常,未发现[QML]节点";
        goto _RET;
    }
    else
    {
        //save_days
        QDomElement temp = parts.firstChildElement("save_days");
        if (temp.isNull())
        {
            bRet = false;
            errMsg = "文件格式异常,未发现[log/save_days]节点";
            goto _RET;
        }
        else
            logDays = temp.text().toInt();

        //print_debug
        temp = parts.firstChildElement("print_debug");
        if (temp.isNull())
        {
            bRet = false;
            errMsg = "文件格式异常,未发现[log/print_debug]节点";
            goto _RET;
        }
        else
        {
            QString ss = temp.text();
            if (ss.compare("true") == 0 || ss.compare("TRUE") == 0)
                printDbgLog = true;
            else
                printDbgLog = false;
        }            
    }

    //Public节点
    parts = root.firstChildElement("Public");
    if (parts.isNull())
    {
        bRet = false;
        errMsg = QString("文件格式异常,未发现[Public]节点");
        goto _RET;
    }
    else
    {
        //remote_time
        QDomElement temp = parts.firstChildElement("remote_time");
        if (temp.isNull())
        {
            bRet = false;
            errMsg = "文件格式异常,未发现[Public/remote_time]节点";
            goto _RET;
        }
        else
            removeTime = temp.text();
    }

    //UPS节点
    parts = root.firstChildElement("UPS");
    if (parts.isNull())
    {
        bRet = false;
        errMsg = QString("文件格式异常,未发现[UPS]节点");
        goto _RET;
    }
    else
    {
        //supplier
        QDomElement temp = parts.firstChildElement("supplier");
        if (temp.isNull())
        {
            bRet = false;
            errMsg = "文件格式异常,未发现[UPS/supplier]节点";
            goto _RET;
        }
        else
            supplier = temp.text();

        //model
        temp = parts.firstChildElement("model");
        if (temp.isNull())
        {
            bRet = false;
            errMsg = "文件格式异常,未发现[UPS/model]节点";
            goto _RET;
        }
        else
            model = temp.text();

        //serialName
        temp = parts.firstChildElement("serialName");
        if (temp.isNull())
        {
            bRet = false;
            errMsg = "文件格式异常,未发现[UPS/serialName]节点";
            goto _RET;
        }
        else
            serialName = temp.text();

        //shutdownPCDelay
        temp = parts.firstChildElement("shutdown_pc_delay");
        if (temp.isNull())
        {
            bRet = false;
            errMsg = "文件格式异常,未发现[UPS/shutdown_pc_delay]节点";
            goto _RET;
        }
        else
            shutdownPCDelay = temp.text().toInt();
    }

    //进程监控节点
    {
        parts = root.firstChildElement("processor_monitor");
        if (parts.isNull())
        {
            bRet = false;
            errMsg = QString("文件格式异常,未发现[processor_monitor]节点");
            goto _RET;
        }
        else
        {
              //循环解析,获取测量点数据
            QDomElement subElement = parts.firstChildElement();
            while (!subElement.isNull())
            {
                if (subElement.tagName().compare("processor") == 0)
                {
                    ProcessorInfo tempValue;
                    if (!GetProcessorInfo(subElement, tempValue))
                    {
                        bRet = false;
                        errMsg = QString("进程监控节点解析失败,%1").arg(errMsg);
                        goto _RET;
                    }
                    else
                        vecProcessorInfo.push_back(tempValue);
                }

                subElement = subElement.nextSiblingElement();
            }           
        }
    }

#pragma endregion

_RET:
    doc.clear();
    return bRet;
}

bool ConfigFile::SaveXMLFile()
{
    bool bRet = true;
    QFile file(filePath);
    QDomDocument doc;           //xml结构

    //以只写入方式打开,并且打开时清空原来内容
    if (!file.open(QFile::WriteOnly | QFile::Truncate))
    {
        errMsg = QString("文件打开失败,%1").arg(file.errorString());
        return false;
    }
   
    //写入xml头部
    QDomProcessingInstruction instruction; //添加处理命令
    instruction = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");
    doc.appendChild(instruction);

    //添加根节点
    QDomElement root = doc.createElement("root");
    doc.appendChild(root);

#pragma region  跟随配置文件记录项改变
    QString strTemp = "";
    QDomText text;

    //写入model节点
    {
        //ups
        QDomElement model = doc.createElement("module");
        QDomElement subNode = doc.createElement("ups");
        if (modeUPS)
            strTemp = "true";
        else
            strTemp = "false";
        text = doc.createTextNode(strTemp);        //设置括号标签中间的值
        subNode.appendChild(text);
        model.appendChild(subNode);

        //process
        subNode = doc.createElement("process");
        if (modeUPS)
            strTemp = "true";
        else
            strTemp = "false";
        text = doc.createTextNode(strTemp);        //设置括号标签中间的值
        subNode.appendChild(text);
        model.appendChild(subNode);
        root.appendChild(model);
    }

    //写入log节点数据
    {
        QDomElement log = doc.createElement("log");
        QDomElement subNode = doc.createElement("save_days");
        strTemp = QString::number(logDays);
        text = doc.createTextNode(strTemp);        //设置括号标签中间的值
        subNode.appendChild(text);
        log.appendChild(subNode);

        subNode = doc.createElement("print_debug");
        if (printDbgLog)
            strTemp = "true";
        else
            strTemp = "false";
        text = doc.createTextNode(strTemp);        //设置括号标签中间的值
        subNode.appendChild(text);
        log.appendChild(subNode);

        root.appendChild(log);
    }

    //写入Public节点数据
    {
        QDomElement ePublic = doc.createElement("Public");
        QDomElement eRemoveTime = doc.createElement("remote_time");
        text = doc.createTextNode(removeTime);        //设置括号标签中间的值
        eRemoveTime.appendChild(text);
        ePublic.appendChild(eRemoveTime);
        root.appendChild(ePublic);
    }

    //写入UPS节点数据
    {
        QDomElement eUPS = doc.createElement("UPS");

        QDomElement eSub = doc.createElement("supplier");
        text = doc.createTextNode(supplier);        //设置括号标签中间的值
        eSub.appendChild(text);
        eUPS.appendChild(eSub);

        eSub = doc.createElement("model");
        text = doc.createTextNode(model);        //设置括号标签中间的值
        eSub.appendChild(text);
        eUPS.appendChild(eSub);

        eSub = doc.createElement("serialName");
        text = doc.createTextNode(serialName);        //设置括号标签中间的值
        eSub.appendChild(text);
        eUPS.appendChild(eSub);

        eSub = doc.createElement("shutdown_pc_delay");
        text = doc.createTextNode(QString::number(shutdownPCDelay));        //设置括号标签中间的值
        eSub.appendChild(text);
        eUPS.appendChild(eSub);

        root.appendChild(eUPS);
    }

    //写入Processor_monitor节点数据
    {
        QDomElement eProcessorList = doc.createElement("processor_monitor");
      
        for (auto it : vecProcessorInfo)
        {
            QDomElement eProcessor = doc.createElement("processor");

            //name
            QDomElement eSub = doc.createElement("name");
            text = doc.createTextNode(it.processorName);        //设置括号标签中间的值
            eSub.appendChild(text);
            eProcessor.appendChild(eSub);

            //path
            eSub = doc.createElement("path");
            text = doc.createTextNode(it.processroPath);        //设置括号标签中间的值
            eSub.appendChild(text);
            eProcessor.appendChild(eSub);

            //autoStart
            if (it.isRestart)
                strTemp = "true";
            else
                strTemp = "false";
            eSub = doc.createElement("autostart");           
            text = doc.createTextNode(strTemp);        //设置括号标签中间的值
            eSub.appendChild(text);
            eProcessor.appendChild(eSub);

            //property
            strTemp = QString::number(it.stopProprity);
            eSub = doc.createElement("proprity");
            text = doc.createTextNode(strTemp);        //设置括号标签中间的值
            eSub.appendChild(text);
            eProcessor.appendChild(eSub);

            eProcessorList.appendChild(eProcessor);
        }

        root.appendChild(eProcessorList);
    }

#pragma endregion

    //输出到文件
    QTextStream out_stream(&file);
    doc.save(out_stream, 4); //缩进4格
    file.close();
    doc.clear();
    return true;
}

//读取一个进程的参数
bool ConfigFile::GetProcessorInfo(const QDomElement& src, ProcessorInfo& value)
{
    QDomElement subElement;

    //name
    subElement = src.firstChildElement("name");
    if (subElement.isNull())
    {
        errMsg = QString("[name]参数不存在");
        return false;
    }
    else
        value.processorName = subElement.text();

    //path
    subElement = src.firstChildElement("path");
    if (subElement.isNull())
    {
        errMsg = QString("[path]参数不存在");
        return false;
    }
    else
        value.processroPath = subElement.text();

    //autostart
    subElement = src.firstChildElement("autostart");
    if (subElement.isNull())
    {
        errMsg = QString("[autostart]参数不存在");
        return false;
    }
    else
    {
        QString strTemp = subElement.text();
        if (strTemp.compare("true") == 0 || strTemp.compare("TRUE") == 0)
            value.isRestart = true;
        else
            value.isRestart = false;
    }

    //property
    subElement = src.firstChildElement("proprity");
    if (subElement.isNull())
    {
        errMsg = QString("[proprity]参数不存在");
        return false;
    }
    else
    {
        QString strTemp = subElement.text();
        value.stopProprity = strTemp.toInt();
        if (value.stopProprity < 0 || value.stopProprity > 2)
            value.stopProprity = 2;
    }

    if (value.processorName.isEmpty())
    {
        errMsg = QString("进程名称为空");
        return false;
    }

    if (value.processroPath.isEmpty())
    {
        errMsg = QString("进程路径为空");
        return false;
    }

    return true;
}

//获取供应商
QString ConfigFile::GetSupplier() { return supplier; }

//获取UPS型号
QString ConfigFile::GetUPSModel() { return model; }

//获取串口名称
QString ConfigFile::GetSerialName() { return serialName; }

//获取延时时间
int ConfigFile::GetDelayMin() { return shutdownPCDelay; }

QString ConfigFile::GetExeDir(void) { return exeDir; }

ConfigFile.cpp

View Code

此类采用单例模式,可供多线程是使用,同时,添加了个人认为很又必要的一些内容。