【问题标题】:How to reset a BufferedInputStream after changing the underlying FileInputStream's position更改底层 FileInputStream 的位置后如何重置 BufferedInputStream
【发布时间】:2017-09-21 20:48:17
【问题描述】:

我正在尝试对文件中的可序列化对象执行随机读/写操作。

我知道我可以设置 FileInputStream 的位置,然后像这样围绕它包裹适当的输入流:

InputStream file = new FileInputStream(fileName);
((FileInputStream)file).getChannel().position(pos);
InputStream buffer = new BufferedInputStream(file);
ObjectInput input = new ObjectInputStream (buffer);

但是如果我以后必须再次更改FileInputStream 的位置以执行另一个写入操作怎么办?

如何更改/重置BufferedInputStream 使其正常运行?我什至必须这样做吗?

另外,我假设这个问题的任何答案也适用于输出场景?!

【问题讨论】:

  • 您将需要使用标记和重置方法。 mark() 方法允许您为某些类型的输入流标记要跳回的点。例如,您可以在读取对象之前标记流。然后重置以能够重新读取它。或者,为此目的使用 RandomAccessFile。
  • @Jason 他在哪里说他只想向后跳?

标签: java serialization inputstream java-io


【解决方案1】:

我同意 EJP:你在这里走错了兔子洞,以防你要求一个适用于任意类的“通用”解决方案。

但是您的 cmets 清楚地表明,您专门设计了您的类,以便在正确使用序列化时产生“大小相等的记录”。

你需要做什么:

  • 忘记使用 FileInput/OutputStream,而是:
  • 使用RandomAccessFile - 因为它允许您在文件中的随机点读取/写入字节
  • 对于每个要处理的对象,使用其自己的、专用的
    ByteArrayInputStream(相应的输出)对象。

换句话说:为了完成这项工作,您不能一次读取/写入多个对象。您希望序列化是一种将单个对象转换为字节流的简单机制;反之亦然。

但请记住:您将在非常有限的用例上花费大量精力。一旦你的类需要变得更复杂,你就崩溃了——因为你现在会得到不同大小的字节表示。

所以真正的答案在这里:虽然您的想法可以实现(并且是关于序列化和文件访问的一个很好的学习练习),但您在这里重新发明了轮子!而且你那个轮子的版本非常有限,很难增强;并可能导致表现平庸。

因此,如果这是针对“真实”产品的:最好转向任何真实数据库,而不是构建自己的 DBMS。

【讨论】:

  • 这里的目标是实现一个 DBMS。所以我会将对象存储在一个文件中,并保留一种指向它们位置的指针。所有对象类都已定义,因此它们具有固定大小。它们都具有基元或固定大小的基元数组的属性。但我明白你的意思。如果我定义类的方式不能保证对象在序列化后具有固定大小,那么我将不得不改变我的方法。
  • @BrunoNascimento 它不能保证,因为有句柄:见我的回答。你将不得不改变你的方法。
  • 顺便说一句:我更新了我的答案,稍后我将用确切的内容替换松散的类名。
  • 我不确定这是否在这里起作用。操作员说他的班级只使用原始值和数组。当您序列化此类对象(一个一个!)时,我希望结果是相同大小的字节记录。
  • 这就是我的观点:您将为每个对象使用唯一的对象输出流进行序列化。该文件包含字节记录,您可以完全自行序列化每个对象。当您将多个对象序列化到该流中时,输出流只能优化句柄。
【解决方案2】:

我认为您可以做的更简单的事情是创建一个新的BufferedInputStream,只要您需要移动FileInputStream 上的位置。

【讨论】:

  • 或任何其他方式。
  • 当然,您还需要在新位置创建一个新的 ObjectInputStream。无论如何,这都是必要的,因为 ObjectInputStream 和 ObjectOutputStream 不会独立处理每个对象——它们维护和使用有关迄今为止看到的所有对象的信息。您想要独立访问的每个对象都需要使用自己的对象流进行序列化或反序列化。
  • @MattTimmermans 并且用新的ObjectOutputStream 将对象序列化到该位置。
【解决方案3】:

我正在尝试对文件中的可序列化对象执行随机读/写操作。

停在那里(然后退后)。你不能通过任何方式做到这一点。对象流依赖于它自己过去的历史,通过一个“句柄”系统,用于类和对象。您通常不会成功,但是您尝试在代码中实现它,例如通过消除缓冲流。

EDIT 序列化不会重新序列化已经序列化的对象。例如,StreamClassDesc,随每个对象提供。相反,它只是为其序列化一个“句柄”。所以即使你只是序列化两个Strings,你也不能自己反序列化第二个,因为classdesc句柄引用流中的前一个对象。请参阅对象序列化规范。还有流头要考虑。

【讨论】:

  • 现在我输入了自己的答案,欢迎提供反馈。
  • @GhostCat 为什么? RandomAccessFile 无关紧要。正如我所说,出于我所说的原因,每个实现可能性都是无关紧要的。
  • 考虑一下...是的,同意。
猜你喜欢
  • 1970-01-01
  • 2020-11-30
  • 2016-02-07
  • 1970-01-01
  • 1970-01-01
  • 2019-10-22
  • 1970-01-01
  • 1970-01-01
  • 2019-04-23
相关资源
最近更新 更多