我需要编写一个函数,它接受某种输入流的东西(例如一个InputStream或一个FileChannel),以便在两次传递中读取一个大文件:一次预先计算一些容量,第二次执行“真正的” “工作。我不希望一次将整个文件加载到内存中(除非它很小)。
是否有适当的Java类提供此功能? FileInputStream本身不支持mark()/ reset()。我认为BufferedInputStream确实如此,但我不清楚是否必须存储整个文件来执行此操作。
C非常简单,只需使用fseek(),ftell()和rewind()即可。 : - (
答案 0 :(得分: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);
}
}
答案 1 :(得分:22)
BufferedInputStream
通过缓冲内存中的内容来支持mark
。它最好保留用于可预测大小的相对较小的预测。
相反,RandomAccessFile
可以直接使用,也可以作为具体InputStream
的基础,并使用rewind()
方法进行扩展。
或者,可以为每个传递打开一个新的FileInputStream
。
答案 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;
答案 3 :(得分:7)
java.nio.channels.FileChannel
有一个方法position(long)
可以将位置重置为零,就像C中的fseek()一样。
答案 4 :(得分:6)
RandomAccessFile就是你想要的:
答案 5 :(得分:2)
查看java.io.RandomAccessFile
答案 6 :(得分:2)
PushbackInputStream也可以工作,只要你知道你想要倒回多少个字符
答案 7 :(得分:2)
BufferedInputStream
有mark(readlimit)
和reset()
。
readlimit
应大于filesize
以使商标有效。
file.length()+1
没问题。
这意味着标记在读取readlimit
字节之前有效,因此您可以返回reset()
。
答案 8 :(得分:2)
你想要的是RandomAccessFileInputStream
- 使用mark / reset实现InputStream
接口,有时基于RandomAccessFiles
进行搜索。存在一些可能满足您需要的实现。
完整源代码的一个示例是http://www.fuin.org/utils4j/index.html,但你会发现许多其他人在互联网上搜索,如果没有完全适合的话,它很容易编码。