当前位置: 首页 > news >正文

Qt中QSettings的键值使用QDataStream进行存储

1. QDataStream介绍

数据流是编码信息的二进制流,与主机的操作系统、CPU 或字节顺序完全无关。例如,Windows 系统下 PC 写入的数据流可由运行 Solaris 的 Sun SPARC 读取。

您还可以使用数据流读/写raw unencoded binary data 。如果需要 "解析 "输入流,请参阅QTextStream 。

QDataStream 类实现了 C++ 基本数据类型的序列化,如char,short,int,char * 等。更复杂数据的序列化是通过将数据分解成原始单元来实现的。

上面内容参考于:QDataStream Class | Qt Core | Qt 6.9.1

2. QSettings介绍

用户通常希望应用程序能跨会话记住其设置(窗口大小和位置、选项等)。在 Windows 系统中,这些信息通常存储在系统注册表中,而在 macOS 和 iOS 中则存储在属性列表文件中。在 Unix 系统上,由于缺乏标准,许多应用程序(包括 KDE 应用程序)都使用 INI 文本文件。

QSettings 是对这些技术的抽象,使您能以可移植的方式保存和恢复应用程序设置。它还支持custom storage formats 。

QSettings 的 API 基于QVariant ,允许您以最小的代价保存大多数基于值的类型,如QString 、QRect 和QImage 。

上面内容参考于:QSettings Class | Qt Core | Qt 6.9.1

3.demo

1. 需求:键值使用QDataStream转换成二进制数据存储到a.ini文件中;同时可以解析a.ini内容到output.ini中。

#include <QCoreApplication>
#include <QSettings>
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QByteArray>// 序列化数据并存储到 a.ini(按 key=value 格式,但值是二进制)
void saveToSettings() {// 1. 准备要序列化的数据QString str = "the answer is";qint32 num = 42;// 2. 分别序列化 str 和 num,并存储到 a.ini 的对应键中QSettings settings("a.ini", QSettings::IniFormat);// 序列化 str 并存储到 text 键QByteArray strData;QDataStream strOut(&strData, QIODevice::WriteOnly);strOut << str;settings.setValue("text", strData);// 序列化 num 并存储到 number 键QByteArray numData;QDataStream numOut(&numData, QIODevice::WriteOnly);numOut << num;settings.setValue("number", numData);qDebug() << "Data serialized and saved to a.ini (Base64 encoded)";
}// 从 a.ini 读取并反序列化,然后写入 output.ini
void loadAndWriteToOutputIni() {// 1. 从 a.iniQSettings settings("a.ini", QSettings::IniFormat);// 读取 text 并反序列化QByteArray strData = (settings.value("text").toByteArray());QDataStream strIn(strData);QString str;strIn >> str;// 读取 number 并反序列化QByteArray numData = (settings.value("number").toByteArray());QDataStream numIn(numData);qint32 num;numIn >> num;// 2. 将解析后的数据写入 output.ini(标准键值对格式)QSettings outputSettings("output.ini", QSettings::IniFormat);outputSettings.setValue("text", str);outputSettings.setValue("number", num);qDebug() << "Deserialized data written to output.ini";qDebug() << "Output String:" << str;qDebug() << "Output Number:" << num;
}int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);saveToSettings();         // 序列化并存储到 a.iniloadAndWriteToOutputIni(); // 从 a.ini 读取并写入 output.inireturn a.exec();
}

运行结果如下:

(1)存入文件a.ini

(2)解析文件output.ini

2.需求:写一个解析a.ini内容所有节、键值对的代码到output.ini中。

#include <QCoreApplication>
#include <QSettings>
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QByteArray>// 反序列化二进制数据(假设存储的是 QString 或 qint32)
QVariant deserializeBinaryData(const QByteArray &data) {QDataStream stream(data);QVariant result;// 尝试反序列化为 QStringQString str;stream >> str;if (!stream.status()) {return str;}// 尝试反序列化为 qint32stream.device()->seek(0); // 重置流位置qint32 num;stream >> num;  //!!!这里数字转换正确了,但是stream转换错误,可能跟字节序有关,处理方法:设置字节序或者数字直接以原始数字存储,非二进制存储。if (stream.status() == QDataStream::Ok) {return num;}// 如果都不是,返回原始 QByteArrayreturn data;
}// 遍历 a.ini 的所有节和键值对,反序列化二进制数据并写入 output.ini
void processIniFile() {// 检查 a.ini 是否存在if (!QFile::exists("a.ini")) {qCritical() << "Error: a.ini does not exist!";return;}QSettings inputSettings("a.ini", QSettings::IniFormat);QSettings outputSettings("output.ini", QSettings::IniFormat);QStringList allKeys = inputSettings.allKeys(); // 例如: ("General/number", "General/text")foreach (const QString &fullKey, allKeys) {// 提取节名和键名int lastSlash = fullKey.lastIndexOf('/');QString section = fullKey.left(lastSlash);QString key = fullKey.mid(lastSlash + 1);// 读取原始数据并反序列化QByteArray rawData = inputSettings.value(fullKey).toByteArray();QVariant value = deserializeBinaryData(rawData);// 写入 output.inioutputSettings.beginGroup(section);outputSettings.setValue(key, value);outputSettings.endGroup();}qDebug() << "Processed a.ini and wrote to output.ini";
}int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);processIniFile(); // 遍历 a.ini 并写入 output.inireturn a.exec();
}

运行结果如下:

上面的内容转出内容有下面问题:

(1)键值对结构错误。

        由于之前使用下面代码,inputSettings.childGroups返回的内容是空,所以使用的上面上面方法遍历键值。

    // 1. 遍历所有节(Sections)QStringList sections = inputSettings.childGroups();foreach (const QString &section, sections) {inputSettings.beginGroup(section);outputSettings.beginGroup(section);// 2. 遍历当前节的所有键QStringList keys = inputSettings.childKeys();foreach (const QString &key, keys) {QByteArray rawData = inputSettings.value(key).toByteArray();QVariant value = deserializeBinaryData(rawData);// 3. 写入 output.ini(保持相同的键值对结构)outputSettings.setValue(key, value);}inputSettings.endGroup();outputSettings.endGroup();}

查阅的资料得到的一种解释是:QSettings 的 childGroups() 行为:
childGroups() 返回的是 当前组(Group)下的子组,而不是当前组本身。
如果你没有调用 beginGroup() 进入某个组,childGroups() 返回的是 顶层组(即 [] 空组的子组)。
如果 a.ini 只有 [General] 这一层,而没有嵌套的子组(如 [General/SubSection]),那么 childGroups() 可能会返回空列表。

(2)数字没有正确解析。

实际测试中stream >> num;  这里数字转换正确了,但是stream转换错误,可能跟字节序有关,处理方法:设置字节序或者数字直接以原始数字存储,而非二进制存储。

查阅的资料得到的一种解释是:从理论上看,二进制流可以设计为完全独立于硬件(如 UTF-8 编码的文本),但实际场景中(如 QDataStream 或网络协议),二进制流的解释往往依赖隐式或显式的字节序约定。

CMakeLists.txt文件如下:

cmake_minimum_required(VERSION 3.5)project(testQDataB LANGUAGES CXX)set(CMAKE_INCLUDE_CURRENT_DIR ON)set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(Qt5Core)add_executable(testQDataBmain.cpp
)
target_link_libraries(testQDataB Qt5::Core)

附加

读写ini也可参考:总结:Qt读写ini配置文件(QSettings)_qt ini-CSDN博客

http://www.xdnf.cn/news/1431883.html

相关文章:

  • 【ComfyUI】SDXL Refiner 提示进一步提升生成图像的质量
  • Android的USB通信 (AOA Android开放配件协议)
  • CSS基础学习步骤
  • 蓝桥杯算法之基础知识(5)
  • GPU 优化 - tensor core 用swizzle 解决bank conflict
  • STM32HAL 快速入门(十六):UART 协议 —— 异步串行通信的底层逻辑
  • PyTorch 训练随机卡死复盘:DataLoader × OpenCV 多进程死锁,三步定位与彻底修复
  • 【lucene】advanceshallow就是遍历跳表的,可以看作是跳表的遍历器
  • vscode下leetcode插件cookie登录
  • MySQL进阶知识梳理
  • 如何用c来编写一个判断闰年平年的微程序呢
  • 静态网站生成利器 Eleventy
  • 大文件稳定上传:Spring Boot + MinIO 断点续传实践
  • leetcode算法刷题的第二十四天
  • 网络数据包是怎么在客户端和服务端之间进行传输的?
  • 【Go语言并发编程:Goroutine调度原理】
  • Flink - 基础学习(1)-三种时间语义
  • PDF翻译怎么弄?一篇文章告诉你答案
  • 线扫相机搭配显微镜:解锁微观世界的 “全景高清” 观察模式
  • go 语言map是线程不安全的如何处理
  • C#实现与西门子S7-1200_1500 PLC通信
  • 【一张图看懂Kafka消息队列架构】
  • AI 在教育领域的落地困境:个性化教学与数据隐私的平衡之道
  • 278-基于Django的协同过滤旅游推荐系统
  • 多个大体积PDF文件怎么按数量批量拆分成多个单独文件
  • sed相关知识
  • 国行 iPhone17 会支持 eSIM 吗?最新爆料与区别解读
  • 华晨宇火星演唱会苏州站连唱三晚 万人狂欢共度浪漫七夕
  • 便携式显示器怎么选?:6大关键指标全解析
  • Windows 命令行:父目录与子目录