序列化std::unordered_map<QString,QString>时出现额外空元素问题
解决QSettings序列化std::unordered_map时出现空键值元素的问题
嘿,我来帮你搞定这个问题!你遇到的额外空键空值元素,本质是读取流时的循环逻辑出错了,咱们一步步分析原因,再给出两种靠谱的解决方案。
问题根源
先看你的读取代码:
while (not in.atEnd()) { in >> key >> val; map.insert_or_assign(std::move(key), std::move(val)); }
in.atEnd()只能告诉你当前流的位置是否在末尾,但它无法预判接下来的读取操作是否会成功。举个例子:当流里的最后一组键值对读完后,流的位置其实还没到末尾(或者说,atEnd()此时返回false),但你再尝试读取key和val时,已经没有足够的数据了——这时候读取操作失败,key和val会被默认初始化为空字符串,而你还是执行了insert_or_assign,于是就多出了那个空元素。
另外,你写入流时只逐个输出键值对,没有记录元素总数,依赖atEnd()判断结束时机本身就不可靠,QDataStream的atEnd()并不适合用来判断结构化数据的读取边界。
解决方案
方案一:写入时记录元素数量(推荐)
这是最稳妥的做法,明确告诉读取端要读多少个元素,完全避免边界判断的问题。
修改写入运算符:
inline QDataStream &operator<<(QDataStream &out, const StrMap &map) { // 先写入元素个数,用quint32保证跨平台兼容性 out << static_cast<quint32>(map.size()); for (auto &&elm : map) { out << elm.first << elm.second; } return out; }
修改读取运算符:
inline QDataStream &operator>>(QDataStream &in, StrMap &map) { quint32 size; in >> size; // 提前预留空间,避免多次扩容,提升性能 map.reserve(size); for (quint32 i = 0; i < size; ++i) { QString key; QString val; in >> key >> val; map.insert_or_assign(std::move(key), std::move(val)); } return in; }
方案二:调整读取循环逻辑,依赖流的读取状态
你提到“QDataStream无法隐式转换为bool”,其实这是个误解——Qt的QDataStream从很早的版本开始就支持operator bool()了,它会返回!in.status(),也就是流状态正常且读取成功时为true。
直接修改读取运算符的循环:
inline QDataStream &operator>>(QDataStream &in, StrMap &map) { QString key; QString val; // 只有当读取key和val都成功时,才进入循环体 while (in >> key >> val) { map.insert_or_assign(std::move(key), std::move(val)); } return in; }
这种写法下,一旦读取失败(比如流里没有更多数据),循环会立刻终止,不会插入空的键值对,也不需要修改写入代码。
验证效果
不管用哪种方案,重新运行代码后,你都会发现输出里的空元素消失了,只会打印你最初插入的四组键值对。
内容的提问来源于stack exchange,提问作者Aykhan Hagverdili
相关产品推荐
相关产品推荐

