Qt基础之配置文件

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

1.INI文件

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

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

  Qt写文件示例如下:

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

 2.XML文件

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

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

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

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

  c.清理内存

  具体示例代码:

  1 //xml类型配置文件读写
  2 bool ConfigFile::LoadXMLFile(QString file_path)
  3 {
  4     bool bRet = true;
  5     //step1:读文件
  6     QFile file(file_path);
  7     if (!file.open(QFile::ReadOnly))
  8     {
  9         errMsg = QString("文件打开失败,%1").arg(file.errorString());
 10         return false;
 11     }
 12  
 13     QString errorStr;
 14     int errorLine;
 15     int errorColumn;
 16  
 17     //qml数据存储格式
 18     QDomDocument doc;
 19     //step2:转换成xml数据格式
 20     if (!doc.setContent(&file, false, &errorStr, &errorLine, &errorColumn))
 21     {
 22         errMsg = QString("QML解析错误,%1,%2,%3").arg(errorStr)
 23              .arg(QString::number(errorLine)).arg(QString::number(errorColumn));
 24          file.close();
 25          return false;
 26     }
 27     //此时已经不需要文件句柄,可关闭
 28     file.close();
 29  
 30     //根节点元素    
 31     QDomElement root = doc.documentElement();
 32  
 33  #pragma region "每个文件不一样,由此处开始修改"
 34      //简单节点结构
 35      QDomElement parts = root.firstChildElement("module");
 36      if (parts.isNull())
 37      {
 38          bRet = false;
 39          errMsg = "文件格式异常,未发现[module]节点";
 40          goto _RET;
 41       }
 42      else
 43      {
 44          //ups
 45          QDomElement temp = parts.firstChildElement("ups");
 46          if (temp.isNull())
 47           {
 48               bRet = false;
 49               errMsg = "文件格式异常,未发现[model/ups]节点";
 50               goto _RET;
 51           }
 52           else
 53           {
 54               QString st = temp.text();
 55               if (st.compare("true") == 0)
 56                   modeUPS = true;
 57               else
 58                   modeUPS = false;
 59           }
 60              
 61         
 62       //多重嵌套、重复结构
 63       {
 64          parts = root.firstChildElement("processor_monitor");
 65          if (parts.isNull())
 66          {
 67              bRet = false;
 68              errMsg = QString("文件格式异常,未发现[processor_monitor]节点");
 69              goto _RET;
 70          }
 71          else
 72          {
 73                //循环解析,获取测量点数据
 74               QDomElement subElement = parts.firstChildElement();
 75               while (!subElement.isNull())
 76               {
 77                   if (subElement.tagName().compare("processor") == 0)
 78                   {
 79                      ProcessorInfo tempValue;
 80                       if (!GetProcessorInfo(subElement, tempValue))
 81                       {
 82                           bRet = false;
 83                           errMsg = QString("进程监控节点解析失败,%1").arg(errMsg);
 84                           goto _RET;
 85                       }
 86                       else
 87                           vecProcessorInfo.push_back(tempValue);
 88                   }
 89   
 90                   subElement = subElement.nextSiblingElement();
 91               }           
 92           }
 93       }
 94   
 95  #pragma endregion
 96   
 97   _RET:
 98       doc.clear();
 99       return bRet;
100 }
View Code

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

  a.创建DOM结构树根节点

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

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

  d.将字符串写入文件

  具体示例代码:

  1 bool ConfigFile::SaveXMLFile()
  2 {
  3     bool bRet = true;
  4     QFile file(filePath);
  5     QDomDocument doc;           //xml结构
  6 
  7     //以只写入方式打开,并且打开时清空原来内容
  8     if (!file.open(QFile::WriteOnly | QFile::Truncate))
  9     {
 10         errMsg = QString("文件打开失败,%1").arg(file.errorString());
 11         return false;
 12     }
 13    
 14     //写入xml头部
 15     QDomProcessingInstruction instruction; //添加处理命令
 16     instruction = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"");
 17     doc.appendChild(instruction);
 18 
 19     //添加根节点
 20     QDomElement root = doc.createElement("root");
 21     doc.appendChild(root);
 22 
 23 #pragma region  跟随配置文件记录项改变
 24     QString strTemp = "";
 25     QDomText text;
 26 
 27     //写入model节点
 28     {
 29         //ups
 30         QDomElement model = doc.createElement("module");
 31         QDomElement subNode = doc.createElement("ups");
 32         if (modeUPS)
 33             strTemp = "true";
 34         else
 35             strTemp = "false";
 36         text = doc.createTextNode(strTemp);        //设置括号标签中间的值
 37         subNode.appendChild(text);
 38         model.appendChild(subNode);
 39 
 40         //process
 41         subNode = doc.createElement("process");
 42         if (modeUPS)
 43             strTemp = "true";
 44         else
 45             strTemp = "false";
 46         text = doc.createTextNode(strTemp);        //设置括号标签中间的值
 47         subNode.appendChild(text);
 48         model.appendChild(subNode);
 49         root.appendChild(model);
 50     }
 51 
 52    
 53     //写入Processor_monitor节点数据
 54     {
 55         QDomElement eProcessorList = doc.createElement("processor_monitor");
 56       
 57         for (auto it : vecProcessorInfo)
 58         {
 59             QDomElement eProcessor = doc.createElement("processor");
 60 
 61             //name
 62             QDomElement eSub = doc.createElement("name");
 63             text = doc.createTextNode(it.processorName);        //设置括号标签中间的值
 64             eSub.appendChild(text);
 65             eProcessor.appendChild(eSub);
 66 
 67             //path
 68             eSub = doc.createElement("path");
 69             text = doc.createTextNode(it.processroPath);        //设置括号标签中间的值
 70             eSub.appendChild(text);
 71             eProcessor.appendChild(eSub);
 72 
 73             //autoStart
 74             if (it.isRestart)
 75                 strTemp = "true";
 76             else
 77                 strTemp = "false";
 78             eSub = doc.createElement("autostart");           
 79             text = doc.createTextNode(strTemp);        //设置括号标签中间的值
 80             eSub.appendChild(text);
 81             eProcessor.appendChild(eSub);
 82 
 83             //property
 84             strTemp = QString::number(it.stopProprity);
 85             eSub = doc.createElement("proprity");
 86             text = doc.createTextNode(strTemp);        //设置括号标签中间的值
 87             eSub.appendChild(text);
 88             eProcessor.appendChild(eSub);
 89 
 90             eProcessorList.appendChild(eProcessor);
 91         }
 92 
 93         root.appendChild(eProcessorList);
 94     }
 95 
 96 #pragma endregion
 97 
 98     //输出到文件
 99     QTextStream out_stream(&file);
100     doc.save(out_stream, 4); //缩进4格
101     file.close();
102     doc.clear()
103     return true;
104 }
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结构:

1 QJsonObject json;
2 json.insert("CMD", QString("GRM"));
3 json.insert("RM", macro_addr);
4 
5 QJsonDocument document;
6 document.setObject(json);
7 QstrJson = document.toJson(QJsonDocument::Compact);
8 if (G_INIFILE->printDbgLog)
9     LOG(INFO) << "发包数据:" << QstrJson.toStdString();
View Code

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

 1 //解析数据
 2     QJsonParseError parseJsonErr;
 3     QJsonDocument document = QJsonDocument::fromJson(qstrRecv.toUtf8(), &parseJsonErr);
 4     if (!(parseJsonErr.error == QJsonParseError::NoError))
 5     {        
 6         errMsg = QString("解析json文件错误,%1").arg(parseJsonErr.errorString());
 7         return false;
 8     }
 9 
10     QJsonObject jsonObject = document.object();
11 
12     //CMD
13     QString temp = jsonObject["CMD"].toString();
14     if (temp.compare("GRM") != 0)
15     {
16         errMsg = "收包命令错误";
17         return false;
18     }
19 
20     //RM
21     int addrIn = jsonObject["RM"].toInt();
22     if (addrIn != macro_addr)
23     {
24         errMsg = "收包Macro地址错误";
25         return false;
26     }
27 
28     //RET
29     if (jsonObject.contains("RET"))
30     {
31         QJsonValue jsonValueList = jsonObject.value("RET");
32         QJsonObject item = jsonValueList.toObject();
33 
34         //ERR
35         int errCode = item["ERR"].toInt();
36         if (errCode != 0)
37         {
38             //MSG
39             QString err = item["MSG"].toString();
40             errMsg = QString("CNC反馈错误,错误码:%1,详情:%2").arg(errCode).arg(err);
41             return false;
42         }
43     }
44 
45     //DATA
46     if (jsonObject.contains("DATA"))
47     {
48         QJsonValue jsonValueList = jsonObject.value("DATA");
49         QJsonObject item = jsonValueList.toObject();
50         macro_value = item["RMV"].toDouble();
51     }
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
#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

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

 

posted @ 2020-11-12 15:51  kyzc  阅读(2591)  评论(0编辑  收藏  举报