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

具有rewind()/reset()功能的java文件输入,java,io,stream,Java,Io,Stream,我需要编写一个函数,它接受某种类型的输入流(例如,InputStream或FileChannel),以便在两次过程中读取一个大文件:一次用于预计算某些容量,另一次用于执行“实际”工作。我不希望整个文件一次加载到内存中(除非它很小) 是否有适当的Java类提供此功能?FileInputStream本身不支持mark()/reset()。我认为BufferedInputStream是这样的,但我不清楚它是否必须存储整个文件才能做到这一点 C非常简单,只需使用fseek()、ftell()和revin

我需要编写一个函数,它接受某种类型的输入流(例如,InputStream或FileChannel),以便在两次过程中读取一个大文件:一次用于预计算某些容量,另一次用于执行“实际”工作。我不希望整个文件一次加载到内存中(除非它很小)

是否有适当的Java类提供此功能?FileInputStream本身不支持mark()/reset()。我认为BufferedInputStream是这样的,但我不清楚它是否必须存储整个文件才能做到这一点


C非常简单,只需使用fseek()、ftell()和revind()-(

查看java.io.RandomAccessFile

java.nio.channels.FileChannel
有一个方法
位置(长)
可以像C中的fseek()一样将位置重置回零。

就是您想要的:

  • fseek()被转换为
  • ftell()被转换为
  • rewind()是seek(0)

BufferedInputStream
通过缓冲内存中的内容来支持
mark
。它最好保留在可预测大小的相对较小的外观头部

相反,
RandomAccessFile
可以直接使用,也可以作为具体的
InputStream
的基础,通过
revind()
方法进行扩展


或者,每次通过都可以打开一个新的
文件输入流

只要您知道要倒带多少个字符,PushbackInputStream也可以工作
BufferedInputStream
具有
标记(读取限制)
重置()
readlimit
应大于
filesize
以使标记有效。
file.length()+1
正常。
这意味着标记在读取
readlimit
字节之前有效,因此您可以通过
reset()返回

您想要的是
随机访问文件InputStream
-实现带有标记/重置的
输入流
接口,有时基于
随机访问文件
进行搜索。有些实现可能会满足您的需要


一个完整的源代码示例在中,但您会发现许多其他人正在搜索internet,如果没有一个源代码完全匹配,则很容易编码。

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

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


fc.position(100);// set the file pointer to byte position 100;

我认为引用FileChannel的答案是正确的

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

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


好的,谢谢。看起来我可以用它打开文件,然后用FileChannel作为类来操作/读/写它。太糟糕了RandomAccessFile没有用它的mark()/reset()方法实现InputStream。>:(您可以相当轻松地滚动自己的文件(如果没有那么优雅的话),请参见示例谢谢,但这是另一个方向(将RandomAccessFile作为InputStream访问).FileChannel是在我的接口中传递的一个OK类。我正在切换到这个答案,因为我需要使用一个可以在常规文件和内存缓冲区之间共享的接口。Grrrrr。我正在编写自己的接口RewindableStream+实现类,其中一个类包装RandomAccessFile。这是目前为止最好的解决方案。BufferedInputt会导致文件的大部分或可能全部被双缓冲。这是一种巨大的浪费。而且RandomAccessFile不会从InputStream继承,因此不能作为您已经使用streams的地方的替代品。然而,这个小类应该非常快且内存效率高。这对我来说非常有效。我添加了一个<代码>标记(0);
到构造函数,因为我在第一次调用
reset()
时遇到了一个“未标记”错误,至少在我的情况下,默认重置位置为0是有意义的。这个解决方案非常有效,只需做一个小改动。我会删除“标记=-1”重置方法内部。用于重置的javadocs没有指示它应该重置标记,只有位置。这允许标记被调用一次,然后重置为多次调用,例如,在执行多次重试时。可能是反驳性问题:为什么这不是FileInputStream的默认行为?Jason,请取消acc接受我的答案并认为它是好的,因为它提供了标准的可标记
InputStream
API的有效实现;
InputStream
的任何消费者都可以使用它,而无需加载整个文件。不正确。
PushbackInputStream#unread(int b)
不倒带
b
字节,但我将
b
推到流的顶部。
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);
    }
}