Microsoft Visual J++. Создание приложений и аплетов на языке Java. Часть 2© Александр Фролов, Григорий ФроловТом 32, М.: Диалог-МИФИ, 1997, 288 стр. Приложение DirectFileAccessДля иллюстрации способов работы с классом RandomAccessFile мы подготовили приложение DirectFileAccess, в котором создается небольшая база данных. Эта база данных состоит из двух файлов: файла данных и файла индекса. В файле данных хранятся записи, сосотящие из двух полей - текстового и числового. Текстовое поле с названием name хранит строки, закрытые смиволами конца строки “\r\n”, а числовое с названием account - значения типа int. Дамп файла данных, создаваемого при первом запуске приложения DirectFileAccess, приведен на рис. 2.11. Рис. 2.11. Дамп файла данных Из этого дампа видно, что после первого запуска приложения в файле данных имеются следующие записи:
При последующих запусках каждый раз в файл данных будут добавляться приведенные выше записи. Так как поле name имеет переменную длину, для обеспечения возможности прямого доступа к записи по ее номеру необходимо где-то хранить смещения всех записей. Мы это делаем в файле индексов, дамп которого на момент после первого запуска приложения представлен на рис. 2.12. Рис. 2.12. Дамп файла индекса Файл индексов хранит 8-байтовые смещения записей файла данных в формате long. Зная номер записи, можнор легко вычислить смещение в файле индексов, по которому хранится смещение нужной записи в файле данных. Если извлечь это смещение, то можно выполнить позиционирование в файле данных с целью чтения нужной записи, что и делает наше приложение. После добавления трех записей в базу данных приложение извлекает три записи в обратном порядке, то есть сначала запись с номером 2, затем с номером 1, и, наконец, с номером 0. Извлеченные записи отображаются в консольном окне приложения (рис. 2.13). Рис. 2.13. Отображение записей базы данных приложением DirectFileAccess Исходные тексты приложенияИсходные тексты приложения DirectFileAccess приведены в листинге 2.9. Листинг 2.9. Файл DirectFileAccess\DirectFileAccess.java // ========================================================= // Прямой доступ к файлу с помощью класса RandomAccessFile // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ========================================================= import java.io.*; import java.util.*; // ========================================================= // Класс DirectFileAccess // Главный класс приложения // ========================================================= public class DirectFileAccess { // ------------------------------------------------------- // main // Метод, получающий управление при запуске приложения // ------------------------------------------------------- public static void main(String args[]) { // Массив для ввода строки с клавиатуры byte bKbdInput[] = new byte[256]; try { // Создаем новую базу данных SimpleDBMS db = new SimpleDBMS( "dbtest.idx", "dbtest.dat"); // Добавляем в нее три записи db.AddRecord("Ivanov", 1000); db.AddRecord("Petrov", 2000); db.AddRecord("Sidoroff", 3000); // Получаем и отображаем содержимое первых трез // записей с номерами 2, 1 и 0 System.out.println(db.GetRecordByNumber(2)); System.out.println(db.GetRecordByNumber(1)); System.out.println(db.GetRecordByNumber(0)); // Закрываем базу данных db.close(); // После ввода любой строки завершаем работу программы System.out.println("Press <Enter> to terminate..."); System.in.read(bKbdInput); } catch(Exception ioe) { System.out.println(ioe.toString()); } } } // ========================================================= // Класс SimpleDBMS // Простейшая база данных // ========================================================= class SimpleDBMS { // Файл индексов RandomAccessFile idx; // Файл данных RandomAccessFile dat; // Значение указателя на текущую запись long idxFilePointer = 0; // ------------------------------------------------------- // SimpleDBMS // Конструктор. Создает и открывает файлы базы данных // ------------------------------------------------------- public SimpleDBMS(String IndexFile, String DataFile) { try { // Создаем и открываем файл индексов idx = new RandomAccessFile(IndexFile, "rw"); // Создаем и открываем файл данных dat = new RandomAccessFile(DataFile, "rw"); } catch(Exception ioe) { System.out.println(ioe.toString()); } } // ------------------------------------------------------- // close // Метод close. Закрывает файлы базы данных // ------------------------------------------------------- public void close() { try { // Закрываем файл индексов idx.close(); // Закрываем файл данных dat.close(); } catch(Exception ioe) { System.out.println(ioe.toString()); } } // ------------------------------------------------------- // AddRecord // Добавление записи в базу данных // ------------------------------------------------------- public void AddRecord(String name, int account) { try { // Устанавливаем текущую позицию в файлах // индекса и данных на конец файла idx.seek(idx.length()); dat.seek(dat.length()); // Получаем смещение в файле данных места, // куда будет добавлена новая запись idxFilePointer = dat.getFilePointer(); // Сохраняем это смещение в файле индексов idx.writeLong(idxFilePointer); // Сохраняем в файле дайнных два поля новой записи dat.writeBytes(name + "\r\n"); dat.writeInt(account); } catch(Exception ioe) { System.out.println(ioe.toString()); } } // ------------------------------------------------------- // GetRecordByNumber // Извлечение записи по ее порядковому номеру // ------------------------------------------------------- public String GetRecordByNumber(long nRec) { // Строка, в которой будет сохранена извлеченная запись String sRecord = "<empty>"; try { // Значение поля account Integer account; // Значение поля name String str = null; // Вычисляем смещение в файле индексов по порядковому // номеру записи idx.seek(nRec * 8); // Извлекаем из файла индексов смещение записи // в файле данных idxFilePointer = idx.readLong(); // Выполняем позиционирование на нужную запись // в файле данных dat.seek(idxFilePointer); // Извлекаем поля записи str = dat.readLine(); account = new Integer(dat.readInt()); // Объединяем значения полей в текстовую строку sRecord = new String("> " + account + ", " + str); } catch(Exception ioe) { System.out.println(ioe.toString()); } // Возвращаем извлеченную запись return sRecord; } } Описание исходных текстовДля работы с базой данных мы создали класс SimpleDBMS, определив в нем конструктор, методы для добавления записей, извлечения записей по их порядковому номеру, а также метод для закрытия базы данных. Метод mainСразу после запуска метод main приложения DirectFileAccess создает базу данных, передавая конструктору имена файла индекса dbtest.idx и файла данных dbtest.dat: SimpleDBMS db = new SimpleDBMS("dbtest.idx", "dbtest.dat"); После этого с помощью метода AddRecord, определенного в классе SimpleDBMS, в базу добавляются три записи, состоящие из текстового и числового полей: db.AddRecord("Ivanov", 1000); db.AddRecord("Petrov", 2000); db.AddRecord("Sidoroff", 3000); Сразу после добавления записей приложение извлекает три записи с номерами 2, 1 и 0, вызывая для этого метод GetRecordByNumber, также определенный в классе SimpleDBMS: System.out.println(db.GetRecordByNumber(2)); System.out.println(db.GetRecordByNumber(1)); System.out.println(db.GetRecordByNumber(0)); Извлеченные записи отображаются на системной консоли. После завершения работы с базой данных она закрывается методом close из класса SimpleDBMS: db.close(); Класс SimpleDBMSРассмотрим теперь класс SimpleDBMS. В этом классе определено три поля с именами idx, dat и idxFilePointer, а также три метода. Поля класса SimpleDBMSПоля idx dat являются объектами класса RandomAccessFile и представляют собой, соответственно, ссылки на файл индекса и файл данных. Поле idxFilePointer типа long используется как рабочее и хранит текущее смещение в файле. Конструктор класса SimpleDBMSКонструктор класса SimpleDBMS выглядит достаточно просто. Все, что он делает, - это создает два объекта класса RandomAccessFile, соответственно, для индекса и данных: idx = new RandomAccessFile(IndexFile, "rw"); dat = new RandomAccessFile(DataFile, "rw"); Так как в качестве второго параметра конструктору класа RandomAccessFile передается строка "rw", файлы открываются и для чтения, и для записи. Метод closeМетод close закрывает файлы индекса и данных, вызывая метод close из класса RandomAccessFile: idx.close(); dat.close(); Метод AddRecordМетод AddRecord добавляет новую запись в конец файла данных, а смещение этой записи - в конец файла индекса. Поэтому перед началом своей работы текущая позиция обоих указанных файлов устанавливается на конец файла. Для установки мы применили метод seek из класса RandomAccessFile, передав ему в качестве параметра значение длины файла в байтах, определенное при помощи метода length из того же класса: idx.seek(idx.length()); dat.seek(dat.length()); Перед тем как добавлять новую запись в файл данных, метод AddRecord определяет текущую позицию в файле данных (в данном случае это позиция конца файла) и записывает эту позицию в файл индекса: idxFilePointer = dat.getFilePointer(); idx.writeLong(idxFilePointer); Далее метод AddRecord выполняет сохранение полей записи в файле данных. Для записи строки вызывается метод writeBytes, а для записи численного значения типа int - метод writeInt: dat.writeBytes(name + "\r\n"); dat.writeInt(account); Обратите внимение, что к строке мы добавляем символы возврата каретки и перевода строки. Это сделано исключительно для того чтобы обозначить конец строки текстового поля. Метод GetRecordByNumberМетод GetRecordByNumber позволяет извлечь произвольную запись из файла данных по ее порядковому номеру. Напомним, что смещения всех записей хранятся в файле индексов и имеют одинаковую длину 8 байт. Пользуясь этим, метод GetRecordByNumber вычисляет смещение в файле индекса простым умножением порядкового номера записи на длину переменной типа long, то есть на 8 байт, а затем выполняет позиционирование: idx.seek(nRec * 8); После этого метод GetRecordByNumber извлекает из файла индексов смещение нужной записи в файле данных, вызывая для этого метод readLong, а затем выполняет позиционирование в файле данных: idxFilePointer = idx.readLong(); dat.seek(idxFilePointer); Поля записи читаются из файла данных в два приема. Вначале читается строка текстового поля, а затем - численное значение, для чего вызываются, соответственно, методы readLine и readInt: str = dat.readLine(); account = new Integer(dat.readInt()); Полученные значения полей объединяются в текстовой строке и записываются в переменную sRecord: sRecord = new String("> " + account + ", " + str); Содержимое этой переменной метод GetRecordByNumber возвращает в качестве извлеченной строки записи базы данных. |