3

我正在使用 protobuf-net 序列化将实时事件附加到文件流中。如何将所有保存的对象流回以供分析?我不想使用内存中的集合(因为它会很大)。

private IEnumerable<Activity> Read() {
  using (var iso = new IsolatedStorageFileStream(storageFilename, FileMode.OpenOrCreate, FileAccess.Read, this.storage))
  using (var sr = new StreamReader(iso)) {
    while (!sr.EndOfStream) {
      yield return Serializer.Deserialize<Activity>(iso); // doesn't work
    }
  }
}

public void Append(Activity activity) {
  using (var iso = new IsolatedStorageFileStream(storageFilename, FileMode.Append, FileAccess.Write, this.storage)) {
    Serializer.Serialize(iso, activity);
  }
}
4

1 回答 1

3

首先,我需要讨论 protobuf 格式(通过 Google,不特定于 protobuf-net)。按照设计,它是可附加的,但带有 append===merge。对于列表,这意味着“作为新项目附加”,但对于单个对象,这意味着“组合成员”。其次,由于上述原因,protobuf 中的根对象永远不会终止——“结束”很简单:当你用完传入的数据时。第三,同样作为直接结果 - 字段不需要按任何特定顺序排列,并且通常会覆盖。所以:如果你只是多次使用序列化,然后读回数据:你将只有一个对象,它基本上具有流中最后一个对象的值。

但是,您想要做的是一个非常常见的场景。所以 protobuf-net 通过包含 SerializeWithLengthPrefix 和 DeserializeWithLengthPrefix 方法来帮助你。如果您使用这些而不是序列化/反序列化,则可以正确解析单个对象。基本上,长度前缀会限制数据,以便仅读取每个对象的确切数量(而不是读取到文件末尾)。

我强烈建议(作为参数)使用 tag===field-number===1 和 base-128 前缀样式(枚举)。除了使数据在整个过程中完全符合 protobuf(包括前缀数据)之外,这还将使使用额外的辅助方法变得容易:DeserializeItems。这通过迭代器块公开每个连续对象,从而可以有效地读取大文件,而无需一次将所有内容都放在内存中。它甚至适用于 LINQ。

还有一种方法可以使用 API 选择性地解析/跳过文件中的不同对象 - 例如,跳过前 532 条记录而不处理数据。如果你需要一个例子,请告诉我。

如果您已经有大量使用 Serialize 而不是 SerializeWithLengthPrefix 存储的数据 - 那么可能仍然可以通过使用 ProtoReader 检测字段编号何时循环回来来破译数据:意思是,给定字段“1, 2 , 4, 5, 1, 3, 2, 5" - 我们大概可以断定那里有 3 个物体并据此破译。再次,让我知道你是否需要一个具体的例子。

于 2013-08-24T18:58:29.480 回答