Чтение объектов из файла произвольного доступа

Я написал файл, используя класс FileChannel Java, который использует RandomAccessFiles. Я написал объекты в разных местах файла. Объекты были разного размера, но все одного класса. Я написал объекты, используя следующую идею:

ByteArrayOutputStream bos= new ByteArrayOutputStream();
        ObjectOutput out = new ObjectOutputStream(bos);
        out.writeObject(r);
        byte[] recordBytes= bos.toByteArray();

    ByteBuffer rbb= ByteBuffer.wrap(recordBytes);

    while(rbb.hasRemaining()) {
        fileChannel.write(rbb);
    }

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

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


person AnkurVj    schedule 12.09.2011    source источник


Ответы (4)


Я должен использовать файлы с произвольным доступом, потому что мне нужно писать в разные позиции в файле.

Нет, не знаешь. Вы можете изменить положение FileOutputStream или FileInputStream через его канал.

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

Object obj = // something

FileOutputStream fos = // an initialized stream

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();

if (bos.size() > MAX_ALLOWED_SIZE)
   throw // or log, or whatever you want to do
else
{
    fos.getChannel().position(writeLocation);
    bos.writeTo(fos);
}

Чтобы прочитать объекты, сделайте следующее:

FileInputStream fis = // an initialized stream

fis.getChannel().position(offsetOfSerializedObject);
ObjectInputStream iis = new ObjectInputStream(new BufferedInputStream(fis));
Object obj = iis.readObject();

Один комментарий: я завернул FileInputStream в BufferedInputStream. В этом конкретном случае, когда файловый поток перемещается перед каждым использованием, это может обеспечить выигрыш в производительности. Имейте в виду, однако, что буферизованный поток может считывать больше байтов, чем необходимо, и в некоторых ситуациях использование потоков объектов с построением по мере необходимости было бы действительно плохой идеей.

person parsifal    schedule 14.09.2011
comment
Жаль, что я потратил столько времени, когда существовало такое прямое решение. Большое спасибо за это превосходное решение! - person AnkurVj; 14.09.2011
comment
Я получаю IOException: ошибка записи: Вы уверены, что здесь все в порядке? - person AnkurVj; 14.09.2011
comment
Хорошо, я реализовал ваше решение без ByteBuffer. Если я закрою ObjectOutputStream, закроется ли базовый поток вывода файлов? Зачем ? - person AnkurVj; 14.09.2011
comment
Да, ObjectOutputStream.close() будет распространяться. Вот почему вы не видите этого в моем примере. Вместо этого я вызываю flush(), чтобы убедиться, что содержимое было записано в поток, а затем позволяю сборщику мусора удалить его. - person parsifal; 14.09.2011
comment
Все работает ! Теперь я могу сказать, что многому научился благодаря этому упражнению. - person AnkurVj; 14.09.2011
comment
Еще не все :( С ObjectInputStream я могу прочитать только один раз, в следующий раз он сообщит о поврежденном потоке: недопустимый код типа .. есть идеи? - person AnkurVj; 14.09.2011
comment
Похоже, вы либо неправильно позиционируете, либо перезаписываете ранее записанные данные. - person parsifal; 14.09.2011
comment
Когда я использовал BufferedInputStream, я столкнулся с этой проблемой, однако при его удалении и непосредственном использовании fileinputstream проблема не появлялась. Пожалуйста помоги. Я действительно хочу использовать BufferedInputStream - person AnkurVj; 14.09.2011
comment
Какое именно смещение? Это количество объектов, которые мы хотим пропустить, или количество байтов? На самом деле запутался между ними. Запись некоторого случайного значения внутрь и попытка чтения объекта приводит к StreamCorruptedException. - person Dhiraj Gandhi; 31.10.2017

Почему ищет работать на вас? Я считаю, что вам нужно seek() исправить местоположения, а затем просто прочитать объекты, используя поток объектов. Кроме того, если вы сохраняете правильное расположение сериализованных объектов, почему вы не сохраняете их размеры? В этом случае вы можете применить ObjectInputStream к байтам, которые вы читаете из файла.

person Andrey Agibalov    schedule 12.09.2011

Самое простое решение, которое приходит на ум, — записать длину массива перед записью самого массива:

while(rbb.hasRemaining()) {
        fileChannel.writeLong(recordBytes.length);
        fileChannel.write(rbb);
    }

При чтении объекта вы сначала читаете длину. Это скажет вам, сколько еще байтов нужно прочитать, чтобы получить ваш объект. Аналогично тому, что вы уже делаете на стороне записи, вы можете прочитать данные в byte[], а затем использовать ByteArrayInputputStream и ObjectInputStream.

person NPE    schedule 12.09.2011
comment
Да, это возможное решение моей проблемы, однако мне было интересно, можно ли это сделать без явного указания размера. Я имею в виду, что когда я использую ObjectInputStream для обычных файлов, мне никогда не нужно указывать размер объекта, который я хочу прочитать! - person AnkurVj; 13.09.2011
comment
В fileChannel нет writeLong, поэтому мне придется сделать это тоже с большим количеством строк кода. - person AnkurVj; 14.09.2011
comment
Кроме того, я не знаю тривиального способа найти размер объекта в Java. - person AnkurVj; 14.09.2011

Вы можете использовать FileInputStream, построенный на объекте RandomAccesFile FileDescriptor, например:

FileDescriptor f = raf.getFD();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));

Предполагая, что RandomAccessFile называется raf.

person bob_twinkles    schedule 12.09.2011
comment
Мой файл произвольного доступа инкапсулирован внутри FileChannel. Как получить объект файла произвольного доступа из этого объекта канала? - person AnkurVj; 13.09.2011
comment
Я не верю, что вы можете - вот ссылка на документ, на всякий случай, если я что-то пропустил. - download.oracle.com/javase/6 /docs/api/java/nio/channels/. Если возможно, было бы лучше, если бы вы просто передали RandomAccessFile методу записи объектов. - person bob_twinkles; 14.09.2011
comment
Предположим, я открыл раф с режимом r. Если я закрою FileInputStream, будет ли закрыт и раф? - person Mostowski Collapse; 03.11.2011
comment
Может быть. То, как я думаю, что виртуальная машина реализует систему ввода-вывода, должно быть, но без гарантий. - person bob_twinkles; 12.12.2011