Re[4]: Перезапись/вставка информации в середину файла
От: nant Россия  
Дата: 28.12.04 17:52
Оценка: 3 (1)
Здравствуйте, iAlexander, Вы писали:

D>>>Никак. Разве что сделать это руками как и в C.


A>Можно, конечно, сделать нативный метод, и в С-ной части кода расширить пространство в файле под объект (не слишком ли хитрый путь...)

Это в принципе невозможно на файловом IO — оно по своему смыслу потоковое и файл являет собой единый кусок.
По крайней мере в SystemV R4, Linux и Win32 я таких функций не видел, так что могу сделать предположение, что таких API не существует в природе. Буду признателен, если переубедишь меня.

A>Ибо я что-то не нашел в Java API, можно ли добавлять в произвольное место потока. Напротив,

A>

A>Public FileOutputStream(String name,
A> boolean append)
A> throws FileNotFoundException
A>Creates an output file stream to write to the file with the specified name. If the second argument is true, then bytes will be written to the end of the file rather than the beginning.


A>А потом, расчистив место, уже делать overwrite. В этом случае уже не удастся передвинуть назад все нижеследующее, если объект уменьшится, ну да это и не важно.


Если ты ведешь разговор про файл произвольного доступа, по которому можно двигаться и читать/писать из/в произвольного(е) места(о),
тогда смотри java.io.RandomAccessFile. Довольно легко написать реализацию InputStream/OutputStream написав абстрактную реализацию методов read и write. Но проблему "вставки в файл" это не решает.


A> — А можно ли узнать количество байт, на которое вырастет описание объекта? (можно, конечно, записать во временный файл Как-то чересчур много гемора получается

Для этого можно записать в java.io.ByteArrayOutputStream:
Object obj=...; //here goes object iniitalization
ByteArrayOutputStream baos = new ByteOutputStream();
new ObjectOutputStream(baos).writeObject(obj);
int newLength = baos.size();


A>Изврат задачи в том, чтобы все вышеописанное хранилось в одном файле Диктую условия не я...


Конечно идеальный вариант это сгенерировать код. Взять что-нибудь типа EMF и написать к нему шаблоны, которые генерируют код сериализации состояния, метод определения размера этого состояния и все прочее. Может быть то же самое можно сделать с помощью XDoclet. Но это требуется знание EMF или же Xdoclet, а готовое решение для довольно нетипичной проблемы тебе вряд ли дадут.

Есть еще вариант — использовать базу данных, которая все свое состояние хранит в одном файле и не требует установки сервера. К примеру HSQLDB. Точнее его In-Process (Standalone) Mode.

Я, как человек весьма ленивый, остановился бы на каком-нибудь из вышеприведенных вариантов (хотя все зависит от условий задачи...)

Но есть решение, которое позволяет пользоваться сериализацией (оно не очень простое — по сути своя файловая система, где в файле содержится один объект, но и не очень сложное).

Нужно разбить файл на блоки размера 2^N, N целое, N>0.
Каждый блок содержит следующую сервисную информацию:
1) N бит — размер реально хранимых данных.
2) 1 бит — признак, занят или свободен данный блок.
3) 64-N бит — положение следующего блока в файле (положение байта в файле в общем случае характеризуется 64-битным числом, нам нужно определить положение каждого 2^N-го байта).
4) 1 бит — признак того, что блок является последним в записи
Итого 66 бит > 8 байт (можно еще поджать где-нибудь, но для выделенных значений N).

Предлагается для упрощения жизни использовать целое число байт для обозначния 1) и 2) вместе и еще целое число байт для обозначния 3) и 4).

Соотвественно имеем 1+N/8 (деление целочисленное) байт на размер данных в блоке и признак занят/не занят, 8-(N-1)/8 (деление целочисленное) байт на "номер" следующего блока. Тут же становится очевидно, что N>=4.
В каждом блоке остается 2^N-9 (при N кратных 8 — 2^N-10) байтов, которые можно использовать под запись информации.

Получаем следующий алгоритм работы:
При записи в файл:
1) Объект сериализуется в байтовый массив. Определяется размер.
2) Ищется первый свободный блок в файле. Если такого нет, то выделяется новый в конце файла.
3) В область, которая доступна для записи информации пишутся данные из массива.
4) Если закончена запись, тогда проставляется соотвествующий флаг.
5) Если нет, тогда ищется новый свободный блок. В записаный только что блок проставляется номер вновь найденного блока. Если такого нет, то выделяется новый в конце файла. Goto 3.


При чтении:
1) Ищется первый блок данного объекта.
2) Читаются данные в байтовый массив
3) Повторять, пока не прочитали последний блок.
4) Десериализовать данные из массива.

Оптимизация: в первом блоке можно также хранить фактическую длину объекта(4 байта), соответственно длинну массива можно подстравивать всего один раз.

При удалении:
1) Ищем первый блок объекта.
2) Помечаем как удаленный
3) Если блок не последний, ищем следующий блок, Goto 2.

Оптимизация:
Для убыстрения операции поиска свободных блоков, предлагается свободные блоки увязывать в список, а в голове файла хранить
Тогда при удалении на шаге 2 нужно найти свободные блоки "слева" (ближе к голове, чем данный) и "справа" (ближе к концу, чем данный).
1) Если "левый" — пустой => первый свободный блок в файле — тот, что удаляется сейчас. Если нет, то "левый".следующий = удаляемый
2) Если "правый" — не пустой, то удаляемый.следующий = "правый".
Короче связный список, он и в Африке связный список

Замечу, что способ не претендует на супероптимальность, так как я не знаю условия всей задачи.

Также возможно:
1) Использовать под хранение сервисной информации число байт, совпадающее с размером каког-либо из целочисленных типов (8,16,32,64).
Это увеличит процент сервисной информации, но упростит чтение.

2) В файле хранить таблицу, которая может по какому-либо идентификатору выдать первый блок в записи объекта (идентификатор уникальный и не зависит от среды исполнения. Это я к тому, что identityHashCode не подойдет). Хранить можно по тому же механизму, используя Hashtable, только уговориться, что первый блок в ее записи обязательно лежит в первом блоке файла (это тот, что с индексом 0).

5) Хранить список свободных блоков в самом файле (тоже где-нибудь в начале).

6) Можно также уменьшить процент сервисной информации, но это ударит по производительности и будет возможно не при всех.

Последние замечания:

При таком подходе часть файла не будет содержать реальной информации. Чем больше размер блока, тем большая часть будет теряться. Но чем меньше размер блока, тем большее число операций позиционирования надо будет сделать — так что упадет производительность.

Работать с таким файлом надо, разумеется, через RandomAccessFile
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.