Ввод Java-файла с возможностью перемотки ()/ сброса ()
Мне нужно написать функцию, которая принимает что-то вроде входного потока (например, InputStream или FileChannel), чтобы читать большой файл за два прохода: один - для предварительного вычисления некоторых мощностей, а второй - для "реальной" работы. Я не хочу, чтобы весь файл загружался в память сразу (если только он не маленький).
Есть ли подходящий класс Java, который предоставляет эту возможность? Сам FileInputStream не поддерживает mark()/reset(). Я думаю, что BufferedInputStream делает, но мне не ясно, нужно ли для этого хранить весь файл.
C настолько прост, что вы просто используете fseek (), ftell () и rewind().:-(
9 ответов
Я думаю, что ответы, ссылающиеся на 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);
}
}
BufferedInputStream
опоры mark
путем буферизации содержимого в памяти. Лучше всего зарезервировать его для относительно небольших прогнозируемых размеров.
Вместо, RandomAccessFile
может быть использован непосредственно, или он может служить основой для конкретного InputStream
, расширенный с rewind()
метод.
В качестве альтернативы, новый FileInputStream
может быть открыт для каждого прохода.
Если вы получите связанный FileChannel
от FileInputStream
Вы можете использовать метод позиционирования, чтобы установить указатель на файл в любом месте файла.
FileInputStream fis = new FileInputStream("/etc/hosts");
FileChannel fc = fis.getChannel();
fc.position(100);// set the file pointer to byte position 100;
java.nio.channels.FileChannel
есть метод position(long)
сбросить позицию обратно в ноль, как fseek() в C.
RandomAccessFile - это то, что вы хотите:
- fseek () переводится в RandomAccessFile # seek
- ftell () переводится в RandomAccessFile#getFilePointer
- перемотка () - поиск (0)
BufferedInputStream
имеет mark(readlimit)
а также reset()
,readlimit
должно быть больше чем filesize
сделать отметку действительной.file.length()+1
все в порядке. Это означает, что знак действителен до readlimit
байты читаются, поэтому вы можете вернуться reset()
,
Что вы хотите RandomAccessFileInputStream
- реализует InputStream
интерфейс с меткой / сбросом, иногда поиск на основе RandomAccessFiles
, Существуют некоторые реализации, которые могут делать то, что вам нужно.
Один пример с источниками приведен на http://www.fuin.org/utils4j/index.html но вы можете найти множество других, ищущих в Интернете, и его достаточно просто кодировать, если ни один из них не подходит точно.
PushbackInputStream также будет работать, если вы знаете, сколько символов вы хотите перематывать