— Привіт, Аміго!

— Привіт, Білаабо! Як життя?

— Чудово. Вчора виводив паразитів, але поки що не дуже виходить. А потім знову довелося ночувати у сміттєвому баку.

— Тобто. все як і раніше відмінно?

— Можна й так би мовити.

— Гуд. А що ми маємо сьогодні?

— Сьогодні я розповім тобі про класRandomAccessFile.

RandomAccessFile і т.д. - 1

Справа в тому, що FileInputStream і FileOutputStream представляють файли у вигляді потоків: читати з них і писати в них можна лише послідовно.

Це не завжди дуже зручно. Іноді тобі потрібно записати кілька рядків у середину файлу, або прочитати кілька сторінок тексту наприкінці багатомегабайтного файлу. Читати весь файл не дуже ефективно.

Для вирішення цієї проблеми було створено клас RandomAccessFile. З його допомогою можна писати в будь-яке місце файлу, читати з нього, а також писати та читати файл одночасно.

— Як цікаво.

— Ага. Дуже зручно насправді.

— А як читати із довільного місця?

— Все досить просто. Уяви, що в тебе відкрито текстовий редактор «Блокнот». У ньому є курсор. Коли ти щось друкуєш, текст додається там, де він стоїть. З читанням файлу те саме. Читання відбувається там, де стоїть «курсор». Під час читання/запису він сам автоматично зсувається.

Давай, я краще покажу тобі приклад:

Читання файлу:
//r- read, файл відкритий тільки для читання
RandomAccessFile raf = new RandomAccessFile("input.txt", "r");

// Переміщаємо "курсор" на 100-й символ.
raf.seek(100);

//читаємо рядок, починаючи з поточного положення курсору і до кінця рядка
String text = raf.readLine();

//закриваємо файл
raf.close();

У цьому прикладі я хотів би звернути твою увагу на дві речі:

По-перше, створення об'єкта RandomAccessFile. Другим параметром йде буква r. Це означає, що файл відкритий для читання (r-read). Якщо ти хочеш відкрити файл для читання та запису, у конструктор треба передати “rw” замість “r”.

По-друге, зверни увагу на метод seek. За допомогою цього методу можна стрибати файлом і змінювати позицію курсора для поточної операції читання/записи. Відразу під час відкриття файлу «курсор» встановлюється на 0-й байт. Або точніше – перед нульовим байтом.

— Чи правильно я зрозумів, що ми відкрили файл, і курсор був у його самому початку – на позиції 0. Потім ми викликали seek і він перемістився на 100-й байт. А коли викликали readLine, то читання вже було починаючи з сотого байта. Так?

— Так. Тільки хочу звернути твою увагу на те, що метод seek дозволяє довільно стрибати по файлу. Приклад:

Читання файлу:
//r- read, файл відкритий тільки для читання
RandomAccessFile raf = new RandomAccessFile("input.txt", "r");
// «Курсор» стоїть на 0-му символі.
String text1 = raf.readLine();

// Переміщаємо "курсор" на 100-й символ.
raf.seek(100);
String text2 = raf.readLine();

// Переміщаємо "курсор" на 0-й символ.
raf.seek(0);
String text3 = raf.readLine();

//закриваємо файл
raf.close();

У цьому прикладі ми спочатку прочитали рядок, починаючи з 0-го байта. Потім стрибнули на сотий байт та прочитали рядок там. Потім знову стрибнули на 0 байт і прочитали рядок. Тобто. text1 та text3 – це ідентичні рядки.

— Ага. Ситуація починає прояснюватись.

— Чудово. Тоді ось тобі ще один приклад:

Читання файлу:
//rw- read/write, файл відкритий для читання і для запису
RandomAccessFile raf = new RandomAccessFile("seek.txt", "rw");

// пишемо у файл рядок, починаючи з 0-го байта
raf.writeBytes("It is a string");

//ставимо курсор на 8-й символ
raf.seek(8);
//друкуємо у файл рядок surprise!
raf.writeBytes("surprise!" );

//закриваємо файл
raf.close();

Тут ми відкриваємо файл для читання та запису – у конструктор передаємо «rw» (read/write ).

Потім пишемо у файл рядок «It is a string».

Потім переставляємо курсор на 8-й байт (якраз на початок слова string)

Потім пишемо у файл рядок «surprise

В результаті файл буде містити «It is a surprise

— Тобто. байти не вставляються в середину файлу, а замінюють ті, що там були?

— Ага.

— А якщо ми встановимо курсор у кінець файлу?

— Тоді байти писатимуться в кінець, а файл – подовжуватимуться. Тобто. практично те саме, коли ти пишеш текст у текстовому редакторі.

— Гм. Начебто все зрозуміло. А чи можна повний список методів класу RandomAccessFile?

— Звичайно. Тримай:

Метод Опис
int read() Читає один байт і повертає його
int read(byte b[], int off, int len) Читає масив байт
int read(byte b[]) Читає масив байт
void readFully(byte b[]) Читає масив байт, чекає, доки додадуться нові байти, якщо їх не вистачає для заповнення масиву
int skipBytes(int n) Пропускає n байт. Тобто. переміщує курсор n байт вперед
void write(int b) Пише один байт у те місце, де стоїть курсор
void write(byte b[]) Пише масив байт у те місце, де стоїть курсор
void write(byte b[], int off, int len) Пише масив байт у те місце, де стоїть курсор
long getFilePointer() Повертає номер байта, який вказує «курсор». Можливо від 0 до «довжини файлу»
void seek(long pos) Переміщує курсор, який використовується для читання/запису, у вказане місце
long length() Повертає довжину файлу
void setLength(long newLength) Встановлює нову довжину файлу. Якщо файл був більшим – він обрізається, якщо менше – розширюється і нове місце заповнюється нулями
void close() Закриває файл
boolean readBoolean() Читає boolean з поточної позиції курсору у файлі
byte readByte() Читає byte з поточної позиції курсору у файлі
char readChar() Читає char з поточної позиції курсору у файлі
int readInt() Читає int з поточної позиції курсору у файлі
long readLong() Читає long з поточної позиції курсору у файлі
float readFloat() Читає float з поточної позиції курсору у файлі
double readDouble() Читає double з поточної позиції курсору у файлі
String readLine() Читає рядок з файлу та повертає його
void writeBoolean(boolean v) Пише boolean у файл (починаючи з позиції курсору)
void writeByte(int v) Пише byte у файл (починаючи з позиції курсору)
void writeChar(int v) Пише char у файл (починаючи з позиції курсору)
void writeInt(int v) Пише int у файл (починаючи з позиції курсору)
void writeLong(long v) Пише long у файл (починаючи з позиції курсору)
void writeFloat(float v) Пише float у файл (починаючи з позиції курсору)
void writeDouble(double v) Пише double у файл (починаючи з позиції курсору)
void writeBytes(String s) Пише рядок у файл (починаючи з позиції курсору)
void writeChars(String s) Пише рядок у файл (починаючи з позиції курсору)

— Гм. Нічого принципово нового. Хіба що пара методів seek()/getFilePointer() та length()/setLength().

— Так, Аміго. Все приблизно те саме. Але ж зручно?

— Зручно. Дякую тобі, Білаабо, за цікаву лекцію і за ті приклади, які ти мені дав.

— Радий допомогти, друже Аміго!