QT_INI

一、QT 自带INI (QSettings)
  1. ini格式

     [section]  #分组/节 
     ;注释     #注释/注解
     key=value #键值对用等号连起来
     key2=123
     
     [section2]
     key=true
  2. QSettings

     //写入
     QSetting setting(path,QSettings::IniFormat);
     setting.setIniCodec("utf-8");
     qDebug()<<"write"<<path;
     setting.setValue("BB/AA","测试");
     setting.setValue("BB/BB",true);
     setting.setValue("BB/CC",123.7);
     setting.setValue("中文/中文","测试");
     //读取
     QSettings setting(path,QSetting::IniFormat);
     setting.setIniCodec("utf-8");
     qDebug()<<"read"<<path;
     qDebug()<<setting.value("BB/AA");
     qDebug()<<setting.value("BB/BB");
     qDebug()<<setting.value("BB/CC");
     qDebug()<<setting.value("中文/中文");
     qDebug()<<setting.value("None/AA");
     
  3. 问题

    注释:清除注释

    缓存:无

    编码:中文编码有问题

    长度:字符串长度有限制

 

二、自定义类
  1. EasyIni.h

 #pragma once
 
 #include <QObject>
 #include <QVariant>
 #include <QList>
 
 namespace Tool {
 
     class EasyIni : public QObject
    {
         Q_OBJECT
     public:
         //操作模式
         enum Mode
        {
             ReadOnly,
             WriteOnly,
             ReadWrite
        };
     public:
         explicit EasyIni(const QString &filepath,
                          EasyIni::Mode mode = EasyIni::ReadWrite,
                          QObject *parent = nullptr);
         ~EasyIni();
 
         //打印ini信息
         void dumpInfo() const;
         //分组列表
         QStringList groups() const;
         //分组内所有key
         QStringList keys(const QString &group) const;
         //获取值
         QVariant value(const QString &group, const QString &key) const;
         //设置值,group或key不存在则创建
         void setValue(const QString &group, const QString &key, const QVariant &value);
         //保存
         void save();
         //加载
         void load(const QString &filepath = QString());
 
     private:
         //判断当前路径是否可写
         bool isWritable() const;
         //解析ini文件
         void parse(const QString &filepath);
         //variant转str copy from qsettings
         static QString variantToString(const QVariant &v);
         //str转variant copy from qsettings
         static QVariant stringToVariant(const QString &s);
         //stringToVariant中用到的分割 copy from qsettings
         static QStringList splitArgs(const QString &s, int idx);
 
     private:
         //ini文件路径
         QString inipath;
         //操作模式
         EasyIni::Mode iniMode = EasyIni::ReadWrite;
 
         //【】数据结构
         //一行的数据
         struct IniRow{
             //是否有效,无效则仅保存在写入时恢复原格式
             bool isValid = false;
             //行key
             QString key;
             //行value
             QVariant value;
        };
         //一个分组的数据
         struct IniGroup{
             //是否为默认初始分组
             bool isHead = false;
             //分组名
             QString group;
             //分组行列表
             QList<IniRow> rows;
             //group key列表
             //无效行为空
             QList<QString> allkeys;
        };
         //ini分组列表
         struct IniData{
             //标记数据改变了,在析构时写入文件
             //或者主动写入文件
             bool change = false;
             //分组数据
             QList<IniGroup> datas;
             //group name列表
             //head和无效为空
             QList<QString> allgroups;
        };
 
         //ini数据
         IniData iniData;
    };
 
 }

 

  1. EasyIni.cpp

 #include "EasyIni.h"
 #include <QTextStream>
 #include <QDataStream>
 #include <QTemporaryFile>
 #include <QFileInfo>
 #include <QFile>
 #include <QDir>
 #include <QRect>
 #include <QSize>
 #include <QDebug>
 
 using namespace Tool;
 
 EasyIni::EasyIni(const QString &filepath, EasyIni::Mode mode, QObject *parent)
    : QObject(parent), inipath(filepath), iniMode(mode)
 {
     load();
 }
 
 EasyIni::~EasyIni()
 {
     save();
 }
 
 void EasyIni::dumpInfo() const
 {
     qDebug()<<"[dump ini info]";
     for(auto agroup:iniData.datas)
    {
         if(!agroup.isHead)
             qDebug()<<"[group]"<<agroup.group;
         for(auto arow:agroup.rows)
        {
             if(arow.isValid){
                 qDebug()<<"\t[key]"<<arow.key<<"[value]"<<arow.value;
            }else{
                 qDebug()<<"\t[unknown]"<<arow.key;
            }
        }
    }
     //qDebug()<<iniData.allgroups;
 }
 
 QStringList EasyIni::groups() const
 {
     auto result = iniData.allgroups;
     result.removeAll(QString());
     return result;
 }
 
 QStringList EasyIni::keys(const QString &group) const
 {
     QStringList key_list;
     int group_index = iniData.allgroups.indexOf(group);
     if(group.isEmpty()||group_index<0)
         return key_list;
     auto result = iniData.datas.at(group_index).allkeys;
     result.removeAll(QString());
     return result;
 }
 
 QVariant EasyIni::value(const QString &group, const QString &key) const
 {
     int group_index = iniData.allgroups.indexOf(group);
     if(group.isEmpty()||key.isEmpty()||group_index<0)
         return QVariant();
 
     int key_index = iniData.datas.at(group_index).allkeys.indexOf(key);
     if(key_index<0)
         return QVariant();
 
     return iniData.datas.at(group_index).rows.at(key_index).value;
 }
 
 void EasyIni::setValue(const QString &group, const QString &key, const QVariant &value)
 {
     if(group.isEmpty()||key.isEmpty())
         return;
     //如果是不存在的分组则创建
     int group_index = iniData.allgroups.indexOf(group);
     if(group_index<0){
         IniGroup ini_group;
         ini_group.isHead = false;
         ini_group.group = group;
         iniData.datas.push_back(ini_group);
         iniData.allgroups.push_back(group);
         group_index = iniData.allgroups.count()-1;
    }
     //如果不存在key则创建
     int key_index = iniData.datas.at(group_index).allkeys.indexOf(key);
     if(key_index<0){
         IniRow ini_row;
         ini_row.isValid = true;
         ini_row.key = key;
         //ini_row.value = value;
         iniData.datas[group_index].rows.push_back(ini_row);
         iniData.datas[group_index].allkeys.push_back(key);
         key_index = iniData.datas.at(group_index).allkeys.count()-1;
    }
     //插入这里有点疑问,就是
     iniData.datas[group_index].rows[key_index].value = value;
     //标记为已修改状态,在save时判断
     iniData.change = true;
 }
 
 void EasyIni::save()
 {
     //只读
     if(EasyIni::ReadOnly == iniMode)
         return;
 
     if(iniData.change && isWritable()){
         qDebug()<<"save ini"<<inipath;
         QFile file(inipath);
         //QIODevice::Text on windows endl=\r\n
         if(file.open(QIODevice::WriteOnly|QIODevice::Truncate|QIODevice::Text))
        {
             QTextStream t_s(&file);
             t_s.setCodec("utf-8");
             for(auto agroup:iniData.datas)
            {
                 if(!agroup.isHead){
                     t_s<<'['<<agroup.group<<']'<<endl;
                }
 
                 for(auto arow:agroup.rows)
                {
                     if(arow.isValid){
                         t_s<<arow.key<<'='<<variantToString(arow.value)<<endl; 
                  }else{ 
                       t_s<<arow.key<<endl; 
                  } 
              } 
          } 
           file.close(); 
      } 
  } 

 
void EasyIni::load(const QString &filepath) 

   //保存当前的 
   save(); 
 
   iniData = IniData{ }; 
   const QString path = filepath.isEmpty()?inipath:filepath; 
   inipath = path; 
 
   QFile file(path); 
   if((EasyIni::WriteOnly == iniMode) || 
           !file.exists() || 
           !file.open(QIODevice::ReadOnly|QIODevice::Text)){ 
       return; 
  } 
 
   qDebug()<<"load ini"<<inipath; 
 
   IniData ini_data; 
   IniGroup ini_group; 
   IniRow ini_row; 
   QTextStream t_s(&file); 
   t_s.setCodec("utf-8"); 
   //qDebug()<<"all"<<t_s.readAll(); 
 
   //默认分组 
   ini_group.isHead=true; 
   while(!t_s.atEnd()){ 
       //去掉首尾空格 
       QString line_data=t_s.readLine().trimmed(); 
       //qDebug()<<"-- line:"<<line_data<<line_data.length(); 
 
       if(line_data.count()<3|| 
               line_data[0]==';'|| 
               line_data[0]=='#'){ 
           //长度小于三无效: a=0 or [a] 注释暂不考虑 
           ini_row.isValid=false; 
           ini_row.key=line_data; 
           ini_row.value.clear(); 
 
           ini_group.rows.push_back(ini_row); 
           ini_group.allkeys.push_back(QString()); 
           //qDebug()<<"invalid"<<line_data; 
      }else if(line_data[0]=='['&& 
                line_data[line_data.length()-1]==']'){ 
           //分组 
           ini_data.datas.push_back(ini_group); 
           ini_data.allgroups.push_back(ini_group.group); 
 
           ini_group.isHead=false; 
           ini_group.group=line_data.mid(1,line_data.count()-2); 
 
           ini_group.rows.clear(); 
           ini_group.allkeys.clear(); 
           //qDebug()<<"group"<<ini_group.group; 
      }else{ 
           ini_row.isValid=false; 
           int split_index=line_data.indexOf('='); 
           if(split_index>0&&split_index<line_data.length()-1){ 
               QString key=line_data.mid(0,split_index).trimmed(); 
               QString value=line_data.mid(split_index+1,line_data.length()-split_index-1).trimmed(); 
               if(!key.isEmpty()&&!value.isEmpty()){ 
                   ini_row.isValid=true; 
                   ini_row.key=key; 
                   ini_row.value=stringToVariant(value); 
 
                   ini_group.rows.push_back(ini_row); 
                   ini_group.allkeys.push_back(key); 
                   //qDebug()<<"key-value"<<key<<value; 
              } 
          } 
           if(!ini_row.isValid){ 
               ini_row.key=line_data; 
               ini_row.value.clear(); 
 
               ini_group.rows.push_back(ini_row); 
               ini_group.allkeys.push_back(QString()); 
               //qDebug()<<"invalid"<<line_data; 
          } 
      } 
  } 
   file.close(); 
 
   //最后一个分组的数据 
   if(ini_group.isHead||(ini_data.allgroups.count()>0&&ini_group.isHead!=true)){ 
       ini_data.datas.push_back(ini_group); 
       ini_data.allgroups.push_back(ini_group.group); 
  } 
 
   iniData = ini_data; 

 
bool EasyIni::isWritable() const 

   bool result=false; 
   QFileInfo fileinfo(inipath); 
   if(fileinfo.exists()){ 
       QFile file(inipath); 
       result=file.open(QFile::ReadWrite); 
       file.close(); 
  }else{ 
       QDir dir(fileinfo.absolutePath()); 
       if(dir.exists()||dir.mkpath(dir.absolutePath())){ 
           //临时文件 
           QTemporaryFile file(inipath); 
           result=file.open(); 
           file.close(); 
      } 
  } 
   return result; 

 
QString EasyIni::variantToString(const QVariant &v) 

   QString result; 
 
   switch (v.type()) { 
   case QVariant::Invalid: 
       result = QLatin1String("@Invalid()"); 
       break; 
 
   case QVariant::ByteArray: { 
       QByteArray a = v.toByteArray(); 
       result = QLatin1String("@ByteArray(") 
               + QLatin1String(a.constData(), a.size()) 
               + QLatin1Char(')'); 
       break; 
  } 
 
   case QVariant::String: 
   case QVariant::LongLong: 
   case QVariant::ULongLong: 
   case QVariant::Int: 
   case QVariant::UInt: 
   case QVariant::Bool: 
   case QVariant::Double: 
   case QVariant::KeySequence: { 
       result = v.toString(); 
       if (result.contains(QChar::Null)) 
           result = QLatin1String("@String(") + result + QLatin1Char(')'); 
       else if (result.startsWith(QLatin1Char('@'))) 
           result.prepend(QLatin1Char('@')); 
       break; 
  } 
#ifndef QT_NO_GEOM_VARIANT 
   case QVariant::Rect: { 
       QRect r = qvariant_cast<QRect>(v); 
       result = QString::asprintf("@Rect(%d %d %d %d)", r.x(), r.y(), r.width(), r.height()); 
       break; 
  } 
   case QVariant::Size: { 
       QSize s = qvariant_cast<QSize>(v); 
       result = QString::asprintf("@Size(%d %d)", s.width(), s.height()); 
       break; 
  } 
   case QVariant::Point: { 
       QPoint p = qvariant_cast<QPoint>(v); 
       result = QString::asprintf("@Point(%d %d)", p.x(), p.y()); 
       break; 
  } 
#endif // !QT_NO_GEOM_VARIANT 
 
   default: { 
#ifndef QT_NO_DATASTREAM 
       QDataStream::Version version; 
       const char *typeSpec; 
       if (v.type() == QVariant::DateTime) { 
           version = QDataStream::Qt_5_6; 
           typeSpec = "@DateTime("; 
      } else { 
           version = QDataStream::Qt_4_0; 
           typeSpec = "@Variant("; 
      } 
       QByteArray a; 
      { 
           QDataStream s(&a, QIODevice::WriteOnly); 
           s.setVersion(version); 
           s << v; 
      } 
 
       result = QLatin1String(typeSpec) 
               + QLatin1String(a.constData(), a.size()) 
               + QLatin1Char(')'); 
#else 
       Q_ASSERT(!"stringToVariant: Cannot save custom types without QDataStream support"); 
#endif 
       break; 
  } 
  } 
 
   return result; 

 
QVariant EasyIni::stringToVariant(const QString &s) 

   if (s.startsWith(QLatin1Char('@'))) { 
       if (s.endsWith(QLatin1Char(')'))) { 
           if (s.startsWith(QLatin1String("@ByteArray("))) { 
               return QVariant(s.midRef(11, s.size() - 12).toLatin1()); 
          } else if (s.startsWith(QLatin1String("@String("))) { 
               return QVariant(s.midRef(8, s.size() - 9).toString()); 
          } else if (s.startsWith(QLatin1String("@Variant(")) 
                      || s.startsWith(QLatin1String("@DateTime("))) { 
#ifndef QT_NO_DATASTREAM 
               QDataStream::Version version; 
               int offset; 
               if (s.at(1) == QLatin1Char('D')) { 
                   version = QDataStream::Qt_5_6; 
                   offset = 10; 
              } else { 
                   version = QDataStream::Qt_4_0; 
                   offset = 9; 
              } 
               QByteArray a = s.midRef(offset).toLatin1(); 
               QDataStream stream(&a, QIODevice::ReadOnly); 
               stream.setVersion(version); 
               QVariant result; 
               stream >> result; 
               return result; 
#else 
               Q_ASSERT(!"stringToVariant: Cannot load custom types without QDataStream support"); 
#endif 
#ifndef QT_NO_GEOM_VARIANT 
          } else if (s.startsWith(QLatin1String("@Rect("))) { 
               QStringList args = splitArgs(s, 5); 
               if (args.size() == 4) 
                   return QVariant(QRect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt())); 
          } else if (s.startsWith(QLatin1String("@Size("))) { 
               QStringList args = splitArgs(s, 5); 
               if (args.size() == 2) 
                   return QVariant(QSize(args[0].toInt(), args[1].toInt())); 
          } else if (s.startsWith(QLatin1String("@Point("))) { 
               QStringList args = splitArgs(s, 6); 
               if (args.size() == 2) 
                   return QVariant(QPoint(args[0].toInt(), args[1].toInt())); 
#endif 
          } else if (s == QLatin1String("@Invalid()")) { 
               return QVariant(); 
          } 
 
      } 
       if (s.startsWith(QLatin1String("@@"))) 
           return QVariant(s.mid(1)); 
  } 
 
   return QVariant(s); 

 
QStringList EasyIni::splitArgs(const QString &s, int idx) 

   int l = s.length(); 
   Q_ASSERT(l > 0); 
   Q_ASSERT(s.at(idx) == QLatin1Char('(')); 
   Q_ASSERT(s.at(l - 1) == QLatin1Char(')')); 
 
   QStringList result; 
   QString item; 
 
   for (++idx; idx < l; ++idx) { 
       QChar c = s.at(idx); 
       if (c == QLatin1Char(')')) { 
           Q_ASSERT(idx == l - 1); 
           result.append(item); 
      } else if (c == QLatin1Char(' ')) { 
           result.append(item); 
           item.clear(); 
      } else { 
           item.append(c); 
      } 
  } 
   return result; 
}

 

posted @ 2021-12-09 09:01  mtgold  阅读(167)  评论(0)    收藏  举报