JavaRush /Курсы /Java Collections /RandomAccessFile и т.д.

RandomAccessFile и т.д.

Java Collections
2 уровень , 1 лекция
Открыта

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

— Привет, Билаабо! Как жизнь?

— Отлично. Вчера выводил паразитов, но пока не очень-то получается. А потом опять пришлось ночевать в мусорном баке.

— Т.е. все по-прежнему отлично?

— Можно и так сказать.

— Гуд. А что у нас будет сегодня?

— Сегодня я тебе расскажу про класс 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().

— Да, Амиго. Все примерно то же самое. Но ведь удобно?

— Удобно. Спасибо тебе, Билаабо, за интересную лекцию и за те примеры, что ты мне дал.

— Рад помочь, друг Амиго!

Комментарии (92)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
CodeMashine Уровень 33
19 мая 2025
а как напечатать что то не перетирая то что было?
{Java_Shark} Уровень 36
17 января 2025
++
hitech Уровень 23
7 сентября 2023
в чем смысл названия класса "RandomAccessFile" ?
Anonymous #2212601 Уровень 39
16 октября 2023
Смысл названия "RandomAccessFile" отражает его способность к произвольному доступу к данным в файлах. Random тут в смысле "произвольный", а не случайный.
hint1k Уровень 51
16 июня 2023
- raf? - неа ruh-roh.
Михаил Уровень 32
9 июня 2023
офф док: public void seek(long pos) throws IOException pos - the offset position, measured in bytes from the beginning of the file, at which to set the file pointer. Была задача, где я этот RAF пихал, а галиматор меня выпихивал. Помню помню...
Dima Makarov Уровень 42
23 марта 2023
А почему мы тут говорим о том, что 1 символ == 1 байт ? На сколько я помню один символ занимает 2 байта. Что-то не сходится у меня в голове.
Даниил Имбер Уровень 31
30 апреля 2023
да хахаах
Popka Уровень 24
21 июля 2023
От кодировки зависит. В Java 1 символ = 2 байтам, но в текстовых редакторах Windows я так понял 1 символ = 1 байт ибо там кодировка на 256 символов
Sergey Bolgov Уровень 26
2 апреля 2024
Смотря какие символы. Английский алфавит будет 1 байт. Русский алфавит 2 байта. "UTF-8 (от англ. Unicode Transformation Format, 8-bit — «формат преобразования Юникода, 8-битный») — одна из общепринятых и стандартизированных кодировок текста, которая позволяет хранить символы Юникода, используя переменное количество байт (от 1 до 6). ОТ 1 до 6 БАЙТ (каждый из которых 8 БИТ)"
Deniel Уровень 42
31 декабря 2022
А зачем нужны FileInputStream, FileOutputStream, если есть такая вот штука полезная.. С Новым Годом, кстати.
7 января 2023
С Новым Годом
Камушек Уровень 36
28 декабря 2023
С наступающим!
Андрей Уровень 51
2 апреля 2024
как один из вариантов - обратная совместимость
aDuVaN4Ik Уровень 42
18 декабря 2022
Откуда в последнем примере после слова "a" появился пробел между словом surprise? Или если пробел 8 символ то запись идет после него, а не вместо?
7 января 2023
[I] [t] [_] [i] [s] [_] [a] [_] string [0] [1] [2] [3] [4] [5] [6] [7] surprise
aDuVaN4Ik Уровень 42
7 января 2023
Понял, спасибо.
Евгений Уровень 38
11 декабря 2022
Кхе-кхе... а зачем существуют метод writeByte(int v), если метод write(int b) делает тоже самое?
Grock Уровень 44
14 февраля 2023
Как гипотеза: метод writeByte(int v) нельзя переопределить, поскольку он final, а метод write(int b) можно.
Стас Уровень 41
14 сентября 2022
А есть спобоб записывать байты в середину файла не переписывая при этом часть файла?
7 января 2023
Хороший вопрос, но "тишина была ему ответом".
Grock Уровень 44
14 февраля 2023
Если есть строки, можно сохранить строку, начиная с курсора, потом вставить новый элемент, а потому "довставить" сохраненную строку (хотя здесь вопрос -- не будет ли наложения на следующую строку..). А вот если весь документ состоит из 1-й строки, то нужно всю строку копировать, что может быть ресурсозатратно по мапяти.
Denis Odesskiy Уровень 47
27 августа 2024
На таких ЯП как java, c#, c++, python, есть 2 способа записи байтов в середину файла и все они практически одинаковы: а) Самый эффективный, создать временный файл:

1) Создание временного файла.
2) Копирование данных из исходного файла до точки вставки.
3) Запись новых байтов.
4) Копирование оставшейся части файла.
5) Замена исходного файла временным файлом.
б) Использование RandomAccessFile для переписывания Этот метод менее эффективен, но позволяет записывать данные в середину файла, перезаписывая только ту часть, которая следует за позицией вставки:

1) Используйте RandomAccessFile, чтобы переместить указатель на позицию вставки.
2) Читайте оставшиеся байты, начиная с позиции вставки, в память.
3) Перезапишите данные на позиции вставки.
4) Запишите оставшиеся байты обратно в файл.
Примечания: Метод создания временного файла является более универсальным и часто предпочтительным, так как он не требует изменения файла на месте и не приводит к повреждению данных при ошибках. Метод с RandomAccessFile может быть полезен для небольших файлов или случаев, когда производительность критична, но следует учитывать потенциальные проблемы с производительностью при больших объемах данных. Таким образом, записывать байты в середину файла не переписывая при этом часть файла нельзя.