【问题标题】:Reading Objects from Random Access File从随机访问文件中读取对象
【发布时间】:2011-11-15 12:03:15
【问题描述】:

我使用 Java 的 FileChannel 类编写了一个文件,该类使用 RandomAccessFiles。我在文件的不同位置写了对象。这些对象大小不一,但都属于同一类。我使用以下想法编写了对象:

ByteArrayOutputStream bos= new ByteArrayOutputStream();
        ObjectOutput out = new ObjectOutputStream(bos);
        out.writeObject(r);
        byte[] recordBytes= bos.toByteArray();

    ByteBuffer rbb= ByteBuffer.wrap(recordBytes);

    while(rbb.hasRemaining()) {
        fileChannel.write(rbb);
    }

现在我想从这样的文件中读取。我不想指定要读取的字节数。我希望能够使用对象输入流直接读取对象。如何做到这一点?

使用随机访问文件,因为我需要写入文件中的不同位置。我还在一个单独的数据结构中记录了写入对象的位置。

【问题讨论】:

    标签: java objectoutputstream filechannel


    【解决方案1】:

    我必须使用随机访问文件,因为我需要写入不同的 文件中的位置。

    不,你没有。您可以通过其频道重新定位FileOutputStreamFileInputStream

    这也将大大简化您的编写代码:您不需要使用缓冲区或通道,并且根据您的需要,您也可以省略ByteArrayOutputStream。但是,正如您在评论中指出的那样,您不会提前知道对象的大小,ByteArrayOutputStream 是验证您没有超出分配空间的有用方法。

    Object obj = // something
    
    FileOutputStream fos = // an initialized stream
    
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);
    oos.flush();
    
    if (bos.size() > MAX_ALLOWED_SIZE)
       throw // or log, or whatever you want to do
    else
    {
        fos.getChannel().position(writeLocation);
        bos.writeTo(fos);
    }
    

    要读取对象,请执行以下操作:

    FileInputStream fis = // an initialized stream
    
    fis.getChannel().position(offsetOfSerializedObject);
    ObjectInputStream iis = new ObjectInputStream(new BufferedInputStream(fis));
    Object obj = iis.readObject();
    

    这里有一条评论:我将FileInputStream 包裹在BufferedInputStream 中。在这种特定情况下,文件流在每次使用之前重新定位,这可以提供性能优势。但是请注意,缓冲流可以读取比需要更多的字节,并且在某些情况下使用按需构造对象流是一个非常糟糕的主意。

    【讨论】:

    • 很遗憾,当存在如此直接的解决方案时,我浪费了这么多时间。非常感谢您提供如此出色的解决方案!
    • 我收到一个 IOException:写入错误:您确定这里一切正常吗?
    • 好的,我在没有 ByteBuffer 的情况下实现了您的解决方案。如果我关闭 ObjectOutputStream,底层文件输出流也会关闭吗?为什么?
    • 是的,ObjectOutputStream.close() 将传播。这就是为什么您在我的示例中看不到它的原因。相反,我调用flush() 以确保内容已写入流,然后让GC 处理它。
    • 一切正常!现在我可以说我从这个练习中学到了很多
    【解决方案2】:

    为什么seek 不适合你?我相信您需要seek() 来更正位置,然后使用您的对象流读取对象。此外,如果您存储序列化对象的正确位置,为什么不存储它们的大小?在这种情况下,您可以对从文件中读取的字节应用 ObjectInputStream

    【讨论】:

      【解决方案3】:

      想到的最简单的解决方案是在写出数组本身之前写出数组的长度:

      while(rbb.hasRemaining()) {
              fileChannel.writeLong(recordBytes.length);
              fileChannel.write(rbb);
          }
      

      读取对象时,首先读取长度。这将告诉您要读取多少字节才能获取您的对象。与您已经在写入方面所做的类似,您可以将数据读入byte[],然后使用ByteArrayInputputStreamObjectInputStream

      【讨论】:

      • 是的,这是我的问题的一个可能的解决方案,但是我想知道这是否可以在没有显式大小写入的情况下完成。我的意思是,当我在普通文件上使用 ObjectInputStream 时,我不必指定要读取的对象的大小!
      • fileChannel 中没有 writeLong,所以我也得用更多的代码行来做
      • 此外,我不知道在 Java 中查找对象大小的简单方法
      【解决方案4】:

      您可以使用在RandomAccesFileFileDescriptor 对象上构造的FileInputStream,如下所示:

      FileDescriptor f = raf.getFD();
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));
      

      假设 RandomAccessFile 被称为 raf。

      【讨论】:

      • 我的随机访问文件封装在FileChannel中。如何从这个通道对象中获取随机访问文件对象?
      • 我不相信你可以 - 这里有一个文档链接,以防我错过了什么。 -download.oracle.com/javase/6/docs/api/java/nio/channels/…。如果可能,最好将RandomAccessFile 传递给写入对象的方法。
      • 假设我用“r”模式打开了 raf。如果我关闭 FileInputStream,raf 也会被关闭吗?
      • 也许吧。我认为 VM 实现 IO 系统的方式应该如此,但没有garentees。
      猜你喜欢
      • 1970-01-01
      • 2018-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多