Вопрос по io, stream, java – Ввод Java-файла с возможностью перемотки () / сброса ()

33

Мне нужно написать функцию, которая принимает что-то вроде входного потока (например, InputStream или FileChannel), чтобы читать большой файл за два прохода: один - для предварительного вычисления некоторых мощностей, а второй - для «реального». Работа. Я не хочу, чтобы весь файл загружался в память одновременно (если только он не маленький).

Is there an appropriate Java class that provides this capability? Сам FileInputStream не поддерживает mark () / reset (). Я думаю, что BufferedInputStream делает, но мне не ясно, нужно ли для этого хранить весь файл.

C настолько прост, что вы просто используете fseek (), ftell () и rewind (). :-(

Джейсон, пожалуйста, не принимай мой ответ и возьмиthis one. Это хорошо, потому что обеспечивает эффективную реализацию стандартного маркируемогоInputStream API; любой потребительInputStream можно использовать без загрузки всего файла. erickson

Ваш Ответ

9   ответов
7

java.nio.channels.FileChannel есть методposition(long) сбросить позицию обратно в ноль, как fseek () в C.

2

PushbackInputStream также будет работать, если вы знаете, сколько символов вы хотите перематывать

Не правда.PushbackInputStream#unread(int b) не перематыватьb байты, но я нажимаюb на вершине потока.
2

Что вы хотитеRandomAccessFileInputStream - реализуетInputStream интерфейс с меткой / сбросом, иногда поиск на основеRandomAccessFiles, Существуют некоторые реализации, которые могут делать то, что вам нужно.

Один пример с источниками приведен вhttp://www.fuin.org/utils4j/index.html но вы найдете много других, ищущих в интернете, и его достаточно легко кодировать, если ни один из них не подходит точно.

23

Я думаю, что ответы, ссылающиеся на 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);
    }
}
Это отлично сработало для меня. Я добавилmark(0); конструктору, потому что я получаю & quot; не помеченный & quot; ошибка при первом вызовеreset() и, по крайней мере в моем случае, имеет смысл для позиции сброса по умолчанию быть 0.
Это решение прекрасно работает, с одним небольшим изменением. Я бы удалил & quot; mark = -1 & quot; внутри метода сброса. Javadocs для сброса не указывают, что он должен сбросить метку, только положение. Это позволяет метке вызываться один раз, а затем сбрасываться, чтобы вызываться несколько раз, например, при выполнении нескольких повторных попыток.
Это, безусловно, лучшее решение. BufferedInput приводит к двойной буферизации больших частей или, возможно, ВСЕХ файлов. Это огромная трата. И RandomAccessFile не наследуется от InputStream, поэтому не может быть заменой тому, где вы уже используете потоки. Этот маленький класс, однако, должен быть чрезвычайно быстрым и эффективным для памяти.
2

BufferedInputStream имеетmark(readlimit) а такжеreset(). readlimit должно быть больше чемfilesize сделать отметку действительной. file.length()+1 все в порядке. Это означает, что знак действителен доreadlimit байты читаются, поэтому вы можете вернутьсяreset().

22

BufferedInputStream опорыmark путем буферизации содержимого в памяти. Лучше всего зарезервировать для относительно небольших прогнозируемых размеров.

Вместо,RandomAccessFile может быть использован непосредственно, или он может служить основой для конкретногоInputStream, расширенный сrewind() метод.

В качестве альтернативы, новыйFileInputStream может быть открыт для каждого прохода.

Я переключаюсь на этот ответ, потому что мне нужно использовать интерфейс, которым я могу поделиться между обычными файлами и буферами в памяти. Grrrrr. Я пишу свой собственный интерфейс RewindableStream + классы реализации, один из которых оборачивает RandomAccessFile. Jason S
2

Проверьте java.io.RandomAccessFile

хорошо спасибо. Похоже, я могу использовать его, чтобы открыть файл, а затем использовать FileChannel в качестве класса для управления / чтения / записи. Jason S
Жаль, что RandomAccessFile не реализует InputStream с его методами mark () / reset (). & GT; :( Jason S
спасибо, но это другое направление (доступ к RandomAccessFile как InputStream). FileChannel - нормальный класс для передачи в моем интерфейсе. Jason S
Вы можете сделать свой собственный довольно легко (если не так элегантно), смотритеcoderanch.com/t/277378/Streams/java/… для примера
6

RandomAccessFile это то, что вы хотите:

fseek() is translated to RandomAccessFile#seek ftell() is translated to RandomAccessFile#getFilePointer rewind() is seek(0)
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;

Похожие вопросы