带有rewind()/ reset()功能的java文件输入

Jas*_*n S 33 java io stream

我需要编写一个函数,它接受某种输入流的东西(例如一个InputStream或一个FileChannel),以便在两次传递中读取一个大文件:一次预先计算一些容量,二次做"真正的"工作.我不希望一次将整个文件加载到内存中(除非它很小).

是否有适当的Java类提供此功能?FileInputStream本身不支持mark()/ reset().我认为BufferedInputStream确实如此,但我不清楚它是否必须存储整个文件来执行此操作.

C很简单,你只需使用fseek(),ftell()和rewind().:-(

yka*_*ich 25

我认为引用FileChannel的答案是标记的.

这是封装此功能的输入流的示例实现.它使用委托,因此它不是真正的FileInputStream,但它是一个InputStream,通常就足够了.如果这是一个要求,可以类似地扩展FileInputStream.

未经测试,使用风险自负:)

public class MarkableFileInputStream extends FilterInputStream {
    private FileChannel myFileChannel;
    private long mark = -1;

    public MarkableFileInputStream(FileInputStream fis) {
        super(fis);
        myFileChannel = fis.getChannel();
    }

    @Override
    public boolean markSupported() {
        return true;
    }

    @Override
    public synchronized void mark(int readlimit) {
        try {
            mark = myFileChannel.position();
        } catch (IOException ex) {
            mark = -1;
        }
    }

    @Override
    public synchronized void reset() throws IOException {
        if (mark == -1) {
            throw new IOException("not marked");
        }
        myFileChannel.position(mark);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 可能是retoretic 问题:为什么这不是FileInputStream 的默认行为? (3认同)
  • 到目前为止,这是最好的解决方案。BufferedInput导致文件的大部分或全部可能被双重缓冲。那是巨大的浪费。而且RandomAccessFile不会从InputStream继承,因此不能取代您已经在使用流的位置。但是,这个小类应该非常快并且具有高效的内存。 (2认同)
  • 这个解决方案效果很好,只有一个小的变化.我会删除reset方法中的"mark = -1".重置的javadoc没有表明它应该重置标记,只有位置.这允许标记被调用一次,然后重置以被多次调用,例如,当执行多次重试时. (2认同)

eri*_*son 22

BufferedInputStreammark通过缓冲内存中的内容来支持.最好保留用于可预测大小的相对较小的预测.

相反,RandomAccessFile可以直接使用,也可以作为具体的基础InputStream,用rewind()方法扩展.

或者,FileInputStream可以为每个通行证打开一个新的.

  • 我正在切换到这个答案,因为我需要使用一个可以在常规文件和内存缓冲区之间共享的接口。咕噜咕噜。我正在编写自己的接口 RewindableStream + 实现类,其中之一包装了 RandomAccessFile。 (2认同)
  • @dotrinh `RandomAccessFile` 可以做 `FileInputStream` 不能做的事情。但是,如果像输入流一样使用,请坚持调用 [`read(byte[])`,](https://docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html #read-byte:A-) 我的猜测是它们的执行方式相同,因为它们进行相同的系统调用。这将是最合理的实现,文档也暗示了这一点。如果需要的话,可以很容易地进行基准测试和确定。 (2认同)

小智 19

如果从中获取了关联FileChannel,则FileInputStream可以使用position方法将文件指针设置为文件中的任何位置.

FileInputStream fis = new FileInputStream("/etc/hosts");
FileChannel     fc = fis.getChannel();


fc.position(100);// set the file pointer to byte position 100;
Run Code Online (Sandbox Code Playgroud)


Arn*_*ter 7

java.nio.channels.FileChannel有一种方法position(long)可以像C中的fseek()那样将位置重置为零.


dfa*_*dfa 6

RandomAccessFile就是你想要的: