• Qt定制化QSettings读写文件的格式


    背景

    在前面的文章中,我们提到,Qt中读写ini文件时存在如下问题:
    1、读取带有逗号的内容时,它会把值解析为数组
    2、读取带有分号的内容时,它会把分号当作结束符。
    但是,在实际的使用中,我们可能就是需要写入带有逗号和分号的内容。那该如何写入呢?

    解决方案

    QSetting提供了自定义读写格式化的方法,开发人员可以传入回调函数readFunc和writeFunc来自定义读写的方法。

    代码

    直接上代码

    构造QSettings对象时传入readIniFile和writeIniFile回调函数。

    const QSettings::Format XmlFormat = QSettings::registerFormat("ini", readIniFile, writeIniFile);
        mSetting = new QSettings("D:/qtlearn/build-QSettingTest-Desktop_Qt_5_12_5_MinGW_32_bit-Debug/vdacfg.ini", XmlFormat, this);
    
    
    • 1
    • 2
    • 3

    readIniFile和writeIniFile的代码如下:

    bool readIniFile(QIODevice &device, QSettings::SettingsMap &settingsMap)
    {
        QString currentSection;
        QTextStream stream(&device);
        stream.setCodec("UTF-8");
        QString data;
        bool ok = true;
        while (!stream.atEnd()) {
            data = stream.readLine();
            if (data.trimmed().isEmpty()) {
                continue;
            }
            if (data[0] == QChar('[')) {
                QString iniSection;
                int inx = data.indexOf(QChar(']'));
                if (inx == -1){
                    ok = false;
                    iniSection = data.mid(1);
                } else {
                    iniSection = data.mid(1, inx - 1);
                }
    
                iniSection = iniSection.trimmed();
                if (iniSection.compare(QString("general"), Qt::CaseInsensitive) == 0) {
                    currentSection.clear();
                } else {
                    if (iniSection.compare(QString("%general"), Qt::CaseInsensitive) == 0) {
                        currentSection = QString("general");
                    } else {
                        currentSection = iniSection;
                    }
                    currentSection += QChar('/');
                }
            } else {
                bool inQuotes = false;
                int equalsPos = -1;
                //QList commaPos;
                 int i = 0;
                while (i < data.size())
                {
                    QChar ch = data.at(i);
                    if (ch == QChar('=')) {
                        if (!inQuotes && equalsPos == -1) {
                            equalsPos = i;
                        }
                    } else if (ch == QChar('"')) {
                        inQuotes = !inQuotes;
                    } /*else if (ch == QChar(',')) {
                        if (!inQuotes && equalsPos != -1) {
                            commaPos.append(i);
                        }
                    } else if (ch == QChar(';') || ch == QChar('#')) {
                        if (!inQuotes) {
                            data.resize(i);
                            break;
                        }
                    }*/ else if (ch == QChar('\\')) {
                        if (++i < data.size()) {
                         } else {
                            ok = false;
                            break;
                        }
                    }
                    i++;
                }
                 if (equalsPos == -1) {
                    break;
                } else {
                    QString key = data.mid(0, equalsPos).trimmed();
                    if (key.isEmpty()) {
                        break;
                    } else {
                        key = currentSection + key;
                    }
                    //if (commaPos.isEmpty()) { //value
                        QString v = data.mid(equalsPos+1).trimmed();
                        if (v.startsWith("\"") && v.endsWith("\"") && v.length()>1) {
                            v = v.mid(1, v.length()-2);                     }
                        settingsMap[key] = stringToVariant(unescapedString(v));
    //                } else { //value list
    //                    commaPos.prepend(equalsPos);
    //                    commaPos.append(-1);
    //                    QVariantList vals;
    //                    for (int i=1; i
    //                        QString d = data.mid(commaPos.at(i-1)+1, commaPos.at(i)-commaPos.at(i-1)-1);
    //                        QString v = d.trimmed();
    //                        if (v.startsWith("\"") && v.endsWith("\"") && v.length()>1) {
    //                            v = v.mid(1, v.length()-2);                         }
    //                        vals.append(stringToVariant(unescapedString(v)));
    //                    }
    //                    settingsMap[key] = vals;
    //                }
                }
            }
        }
         return ok;
    }
    
    
    bool writeIniFile(QIODevice &device, const QSettings::SettingsMap &settingsMap)
    {
    #ifdef Q_OS_WIN
        const char * const eol = "\r\n";
    #else
        const char eol = '\n';
    #endif
        bool writeError = false;
    
        QString lastSection;
        QMapIterator<QString,QVariant> it(settingsMap);
        while(it.hasNext() && !writeError) {
            it.next();
            QString key = it.key();
            QString section;
            qDebug()<<"key: "<<key;
            int idx = key.lastIndexOf(QChar('/'));
            if (idx == -1) {
                section = QString("[General]");
            } else {
                section = key.left(idx);
                key = key.mid(idx+1);
                if (section.compare(QString("General"), Qt::CaseInsensitive) == 0) {
                    section = QString("[%General]");
                } else {
                    section.prepend(QChar('['));
                    section.append(QChar(']'));
                }
            }
    
            if (section.compare(lastSection, Qt::CaseInsensitive))
            {
                if (!lastSection.isEmpty()) {
                    device.write(eol);
                }
                lastSection = section;
                if (device.write(section.toUtf8() + eol) == -1) {
                    writeError = true;
                    qDebug() << "writeError = true ---1";
                }
            }
            QByteArray block = key.toUtf8();
            block += " = ";
            if (it.value().type() == QVariant::StringList) {
                foreach (QString s, it.value().toStringList()) {
                    block += escapedString(s);
                    block += ", ";
                }
                if (block.endsWith(", ")) {
                    block.chop(2);
                }
            } else if (it.value().type() == QVariant::List) {
                foreach (QVariant v, it.value().toList()) {
                    block += escapedString(variantToString(v));
                    block += ", ";
                }
                if (block.endsWith(", ")) {
                    block.chop(2);
                }
            } else {
                block += /*escapedString*/(variantToString(it.value()));
            }
            block += eol;
    
            if (device.write(block) == -1) {
                writeError = true;
                qDebug() << "writeError = true ---2";
            }
        }
    
        return !writeError;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171

    几个辅助函数如下:

    const char hexDigits[] = "0123456789ABCDEF";
    
    QString unescapedString(const QString &src)
    {
        static const char escapeCodes[][2] =
        {
            { 'a', '\a' },
            { 'b', '\b' },
            { 'f', '\f' },
            { 'n', '\n' },
            { 'r', '\r' },
            { 't', '\t' },
            { 'v', '\v' },
            { '"', '"' },
            { '?', '?' },
            { '\'', '\'' },
            { '\\', '\\' }
        };
        static const int numEscapeCodes = sizeof(escapeCodes) / sizeof(escapeCodes[0]);
    
        QString stringResult;
        int escapeVal = 0;
        QChar ch;
        int i = 0;
    normal:
        while (i < src.size()) {
            ch = src.at(i);
            if (ch == QChar('\\')) {
                ++i;
                if (i >= src.size()) {
                    break;
                }
                ch = src.at(i++);
                for (int j = 0; j < numEscapeCodes; ++j) {
                    if (ch == escapeCodes[j][0]) {
                        stringResult += QChar(escapeCodes[j][1]);
                        goto normal;
                    }
                }
                if (ch == 'x') {
                    escapeVal = 0;
                    if (i >= src.size())
                        break;
                    ch = src.at(i);
                    if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
                        goto hexEscape;
                } else if (ch >= '0' && ch <= '7') {
                    escapeVal = ch.unicode() - '0';
                    goto octEscape;
                } else {
                    //skip
                }
            } else {
                stringResult += ch;
            }
                    i++;
        }
        goto end;
    
    hexEscape:
        if (i >= src.size()) {
            stringResult += QChar(escapeVal);
            goto end;
        }
    
        ch = src.at(i);
        if (ch >= 'a')
            ch = ch.unicode() - ('a' - 'A');
        if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')) {
            escapeVal <<= 4;
            escapeVal += strchr(hexDigits, ch.toLatin1()) - hexDigits;
            ++i;
            goto hexEscape;
        } else {
            stringResult += QChar(escapeVal);
            goto normal;
        }
    
    octEscape:
        if (i >= src.size()) {
            stringResult += QChar(escapeVal);
            goto end;
        }
    
        ch = src.at(i);
        if (ch >= '0' && ch <= '7') {
            escapeVal <<= 3;
            escapeVal += ch.toLatin1() - '0';
            ++i;
            goto octEscape;
        } else {
            stringResult += QChar(escapeVal);
            goto normal;
        }
    
    end:
        return stringResult;
    }
    
    QString variantToString(const QVariant &v)
    {
        QString result;
        switch (v.type()) {
            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.startsWith(QChar('@')))
                    result.prepend(QChar('@'));
                break;
            }
            default: {
                QByteArray a;
                {
                    QDataStream s(&a, QIODevice::WriteOnly);
                    s.setVersion(QDataStream::Qt_4_0);
                    s << v;
                }
    
                result = QString("@Variant(");
                result += QString::fromLatin1(a.constData(), a.size());
                result += QChar(')');
                break;
            }
        }
    
        return result;
    }
    
    QByteArray escapedString(const QString &src)
    {
        bool needsQuotes = false;
        bool escapeNextIfDigit = false;
        int i;
        QByteArray result;
        result.reserve(src.size() * 3 / 2);
        for (i = 0; i < src.size(); ++i) {
            uint ch = src.at(i).unicode();
            if (ch == ';' || ch == ',' || ch == '=' || ch == '#') {
                needsQuotes = true;
            }
            if (escapeNextIfDigit && ((ch >= '0' && ch <= '9')
                     || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))) {
                result += "\\x";
                result += QByteArray::number(ch, 16);
                continue;
            }
    
            escapeNextIfDigit = false;
    
            switch (ch) {
            case '\0':
                result += "\\0";
                escapeNextIfDigit = true;
                break;
            case '\n':
                result += "\\n";
                break;
            case '\r':
                result += "\\r";
                break;
            case '\t':
                result += "\\t";
                break;
            case '"':
            case '\\':
                result += '\\';
                result += (char)ch;
                break;
            default:
                if (ch <= 0x1F) {
                    result += "\\x";
                    result += QByteArray::number(ch, 16);
                    escapeNextIfDigit = true;
                } else{
                    result += QString(src[i]).toUtf8();
                }
            }
        }
        if (result.size()>0 && (result.at(0)==' ' || result.at(result.size() - 1) == ' ')) {
            needsQuotes = true;
        }
        if (needsQuotes) {
            result.prepend('"');
            result.append('"');
        }
        return result;
    }
    
    QVariant stringToVariant(const QString &s)
    {
        if (s.startsWith(QChar('@'))) {
            if (s.endsWith(QChar(')'))) {
                if (s.startsWith(QString("@Variant("))) {
                    QByteArray a(s.toUtf8().mid(9));
                    QDataStream stream(&a, QIODevice::ReadOnly);
                    stream.setVersion(QDataStream::Qt_4_0);
                    QVariant result;
                    stream >> result;
                    return result;
                }
            }
            if (s.startsWith(QString("@@")))
                return QVariant(s.mid(1));
        }
        return QVariant(s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212

    测试调用代码如下:

    mSetting->beginGroup("student");
    mSetting->setValue("city", "深圳");
    mSetting->endGroup();
    
    mSetting->setValue("student/name", "xiongfei");
    mSetting->setValue("student/age", "24");
    mSetting->setValue("student/sex", "女");
    
    mSetting->setValue("student/mynoteids", "4;1,2,3,4");
    
    mSetting->setValue("mynoteids", "4;1,2,3,4");
    
    mSetting->setValue("teacher/name", "刘春花");
    mSetting->setValue("teacher/age", "42");
    mSetting->setValue("teacher/major", "语文");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    vdacfg.ini文件效果如下:

    [General]
    mynoteids = 4;1,2,3,4

    [student]
    age = 24
    city = 广州
    mynoteids = 4;1,2,3,4
    name = xiongfei
    noteids = 1,2,3
    sex = 男

    [teacher]
    age = 3
    major = 应用
    name = xiongxiaoya

  • 相关阅读:
    【MySQL】MySql常见面试题总结
    GA分析的智能目标有什么用?
    并发程序设计,你真的懂吗?
    知识产权维权类型有哪些
    leetcode1:两数之和
    c++实现多重继承
    k8s-部署rancher-页面化管理
    线上教育系统平台,企业如何才能运营呢?
    天下苦定制久矣,平台化建设到底难在哪里?
    JAVA:在IDEA引入本地jar包的方法并解决打包scope为system时发布无法打包进lib的方案
  • 原文地址:https://blog.csdn.net/xiongpursuit88/article/details/127926866