【问题标题】:How to compare two parsed files and append the difference to the first?如何比较两个解析的文件并将差异附加到第一个?
【发布时间】:2015-10-25 23:20:31
【问题描述】:

假设我有两个 .dat 文件;一个在我的电脑上,另一个在地球的另一边——数据通过QDataStream不断被序列化到它们中。

数据的解析方式相同——首先是某种 ID,然后是与该特定 ID 关联的对象。

QFile file("data.dat");
QDataStream stream(&file);

file.open("QIODevice::ReadWrite");

stream << *id*;          // ID goes in.
stream << *data_object*; // Object with interesting data is serialized into the file.

file.close();

一段时间后 - 第一个可能看起来像这样(说明性,语法不正确):

//-------------------------------------DATA.DAT------------------------------------//

ID:873482025
 dataObject

ID:129845379
 dataObject

ID:836482455
 dataObject

ID:224964811
 dataObject

ID:625444876
 dataObject

ID:215548669
 dataObject

//-------------------------------------DATA.DAT------------------------------------//

但第二个还没有完全赶上。

//-------------------------------------DATA.DAT------------------------------------//

ID:873482025
 dataObject

ID:129845379
 dataObject

ID:836482455
 dataObject

//-------------------------------------DATA.DAT------------------------------------//

是否可以同时获取两个文件——检测它们之间的差异,然后“融合”第二个文件中缺少但第一个文件中存在的文件?

显然,这可以通过编写一个函数来提取文件的内部结构、对内容进行单独分类、比较它们等等来实现——但是有没有办法通过只处理文件本身来做到这一点,而不必解析单独的内容?

【问题讨论】:

  • 鉴于“解析器”是微不足道的,并且已经编写了数据流运算符,有什么问题?这似乎是一件非常微不足道的事情,除非我错过了什么。
  • ID 和对象之间是否存在 1:1 的映射关系?也就是说,只要ID相等,对象也保证相等吗?
  • 当您说 1:1 映射时,您的意思是“每个 ID 一个对象”吗?如果是这样,是的——每个 ID 只有一个对象。每个对象都有一组与收集的数据相关联的唯一私有变量。与其说是功能,不如说是“有可能”——在理论上。
  • 抱歉,现在我明白你的意思了:如果两个条目具有相同的 ID,则对象中包含的信息将始终相同。

标签: c++ qt serialization qdatastream


【解决方案1】:
  1. 读取这两个文件以提取 Id 集。

  2. 读取其中一个文件,同时将缺少 Id 的对象附加到另一个文件中。

您可以利用QSet 进行集合算术。此外,每个对象不仅需要流式操作符,还需要 skipObject 静态方法。我也忽略了您如何区分对象类型。

typedef qint32_t Id;

bool isOk(const QDataStream & str) { return str.status() == QDataStream::Ok; }

class Object {
  ...
public:
  static void skipObject(QDataStream & str) {
    qint8 format;
    str >> format;
    if (format == 0)
      str.skipRawData(32); // e.g. format 0 of this object is 32 bytes long
    ...
  }
};

QPair<QSet<Id>, bool> getIds(const QString & path) {
  QSet<Id> ids;
  QFile file(path);
  if (!file.open(QIODevice::ReadOnly)) return ids;
  QDataStream stream(&file);
  while (!stream.atEnd()) {
    stream >> id;
    Object::skipObject(stream);
    if (ids.contains(id))
      qWarning() << "duplicate id" << id << "in" << path;
    ids.insert(id);
  }
  return qMakePair(ids, isOk(stream));
}

bool copyIds(const QString & src, const QString & dst, const QSet<Id> & ids) {
  QFile fSrc(src), fDst(dst);
  if (! fSrc.open(QIODevice::ReadOnly)) return false;
  if (! fDst.open(QIODevice::WriteOnly | QIODevice::Append)) return false;
  QDataStream sSrc(&fSrc), sDst(&fDst);
  while (!sSrc.atEnd()) {
    Id id;
    sSrc >> id;
    if (ids.contains(id)) {
       Object object;
       sSrc >> object;
       sDst << id << object;
    } else
       Object::skipObject(sSrc);     
  }
  return isOk(sSrc) && isOk(sDst);
}

bool copyIds(const QString & src, const QString & dst) {
  auto idsSrc = getIds(src);
  auto idsDst = getIds(dst);
  if (!idsSrc.second || !idsDst.second) return false;
  auto ids = idsSrc.first - idsDst.first; 
  return copyIds(src, dst, ids);
}

【讨论】:

  • 旁注:如果每个对象还包含更大的东西,比如 QImage,你会采取什么优化措施?
  • @Quoi 在内部,我确定您的对象存储它们的大小。您应该可以使用QDataStream::skipRawData 跳过您不打算保留的对象。
  • @Quoi 您可能希望参考 Serializing Qt Data Types 帮助主题和 QImage 的源代码以了解如何跳过图像。
  • 很抱歉在 cmets 中一直在开玩笑——但是说我想打开文件并将具有特定 ID 的对象换成新的。这可能吗?
  • @Quoi 如果新对象的大小相同,那么这很简单。寻找过去的 id,并写一个新的对象。否则: 1. 用无效 ID 重写 ID,这意味着忽略该对象,并在末尾附加具有正确 ID 的新对象。 2. 将所有内容复制到一个新文件中,同时添加您想要的对象。最后,由于这变得复杂,使用 sqlite 并将对象转储为序列化 blob 会更简单,让 sqlite 担心确保磁盘文件不会损坏。 Sqlite 非常擅长这一点 - 比你在一周内编写的任何代码都要好。
猜你喜欢
  • 1970-01-01
  • 2020-06-13
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多