Хороший дизайн: как передать InputStreams в качестве аргумента?

У меня есть большой файл, в котором я открываю FileInputStream. Этот файл содержит несколько файлов, каждый из которых имеет смещение от начала и размер. Кроме того, у меня есть парсер, который должен оценивать такой содержащийся файл.

File file = ...; // the big file
long offset = 1734; // a contained file's offset
long size = 256; // a contained file's size
FileInputStream fis = new FileInputStream(file );
fis.skip(offset);
parse(fis, size);

public void parse(InputStream is, long size) {
   // parse stream data and insure we don't read more than size bytes
   is.close();
}

Я чувствую, что это не очень хорошая практика. Есть ли лучший способ сделать это, возможно, с использованием буферизации?

Кроме того, я чувствую, что метод skip () сильно замедляет процесс чтения.

14.12.2008 19:41:24
5 ОТВЕТОВ
РЕШЕНИЕ

Похоже, что вы действительно хотите, это своего рода «частичный» поток ввода - немного похожий на ZipInputStream, где у вас есть поток внутри потока.

Вы можете написать это самостоятельно, проксируя все методы InputStream в исходном входном потоке, внося соответствующие корректировки для смещения и проверяя чтение после конца подфайла.

Вы говорите об этом?

5
14.12.2008 19:49:38
Попытался создать подкласс FileInputStream для создания пользовательского InputStream для моего случая. Кажется, что FIS ищет символ EOF, которого на самом деле нет. Я проверил это в SVN: code.google.com/p/mtmx/source/browse/code/core/trunk/mtmx.file/…
Stefan Teitge 14.12.2008 20:57:05
Я не стал бы создавать подкласс FileInputStream - я бы создал подкласс только InputStream, поэтому вы можете создать частичный поток из любого входного потока.
Jon Skeet 14.12.2008 21:20:00
Поменял его, я проксировал FileInputStream и теперь он работает. Он проверил это, если вы хотите посмотреть. Всем спасибо.
Stefan Teitge 14.12.2008 21:21:48
Зачем вообще ограничивать его FileInputStream? Сам прокси InputStream. Также ваше чтение (byte [] b) в данный момент неверно - доступно - = b.length; должен быть доступен - = читать;
Jon Skeet 14.12.2008 21:32:10
Подкласс java.io.FilterInputStream, а не InputStream.
erickson 15.12.2008 00:32:55

Это звучит как типичная проблема с вложенным файлом, называемая «файлом zip».

Обычный способ справиться с этим - фактически иметь отдельный экземпляр InputStream для каждого вложенного логического потока. Они будут выполнять необходимые операции с базовым физическим потоком, и буферизация может осуществляться как в базовом потоке, так и в логическом потоке, в зависимости от того, какой из них подходит лучше всего. Это означает, что логический поток инкапсулирует всю информацию о размещении в базовом потоке.

Например, у вас может быть такой фабричный метод, который будет иметь такую ​​подпись:

List<InputStream> getStreams(File inputFile)

Вы можете сделать то же самое с OutputStreams.

Есть некоторые детали этого, но этого может быть достаточно для вас?

2
14.12.2008 19:48:44

Вы можете использовать класс-оболочку для RandomAccessFile - попробуйте это

Вы также можете попробовать обернуть это в BufferedInputStream и посмотреть, улучшится ли производительность.

0
14.12.2008 19:48:55

Во-первых, в FileInputStream.skip () есть ошибка, из-за которой файл, находящийся под ним, может пропустить за пределы маркера EOF файла, так что будьте осторожны с ним.

Я лично нашел, что работа с Input / OutputStreams - это боль по сравнению с использованием FileReader и FileWriter, и вы показываете основную проблему, которая у меня есть: необходимость закрыть потоки после использования. Одна из проблем заключается в том, что вы никогда не можете быть уверены, что правильно закрыли все ресурсы, если не сделаете код слишком осторожным, как это:

public void parse(File in, long size) {
    try {
        FileInputStream fis = new FileInputStream(in);
        // do file content handling here
    } finally {
        fis.close();
    }
    // do parsing here
}

Это, конечно, плохо в том смысле, что это приведет к постоянному созданию новых объектов, которые могут в конечном итоге потреблять много ресурсов. Хорошей стороной этого является, конечно, то, что поток закроется, даже если код обработки файла выдает исключение.

3
14.12.2008 19:59:17
Я не понимаю ваш комментарий о FileInputStream против FileReader. В обоих случаях вам необходимо закрыть ресурс после окончания работы с ним. Единственное отличие состоит в том, что поток имеет дело с двоичными данными, а Reader - с текстом.
Jon Skeet 14.12.2008 20:28:44
FileReader / FileWriter - это просто вспомогательные слои поверх FileInputStream / FileOutputStream, где выполняется преобразование из октетов (байтов) в символы. Я даже не думаю, что вы можете выбрать тип конверсии!
Adrian Pronk 14.12.2008 21:50:14
Jon: Это небольшое удобство, но это все же намного лучше, чем возиться с потоками напрямую. Как я уже сказал, есть несколько проблем, которые мне не нравятся в потоковом вводе-выводе Java, такие как системно-зависимое изменение строки, которое, к счастью, может быть нейтрализовано с помощью BufferedWriter.nextLine () и BufferedReader.readLine ().
Esko 15.12.2008 06:56:21

Как правило, код, открывающий файл, должен закрывать файл - функция parse () не должна закрывать входной поток, поскольку для нее крайне высокомерно предполагать, что остальная часть программы не захочет продолжать чтение других файлов, содержащихся в большом.

Вы должны решить, должен ли интерфейс parse () быть только потоком и длиной (с функцией, способной предполагать, что файл правильно расположен) или должен ли интерфейс включать смещение (таким образом, функция сначала позиционирует, а затем читает). Обе конструкции возможны. Я был бы склонен позволить parse () делать позиционирование, но это не однозначное решение.

1
14.12.2008 21:38:00