В прошлом я говорил о RandomAccessFile
и о том, как его можно использовать для ускорения ввода-вывода в Java, а в этом руководстве по Java NIO мы увидим, как использовать данные для чтения / записи при использовании FileChannel и ByteBuffer .
Канал предоставляет альтернативный способ чтения данных из файла, он обеспечивает лучшую производительность, чем InputStream или OutputStream. Его также можно открыть в режиме блокировки и неблокирования. Хотя FileChannles являются каналами чтения / записи, и они всегда блокируют , их нельзя перевести в неблокирующий режим. Класс RandomAccessFile обрабатывает файл как массив байтов.
Вы можете записать свои данные в любую позицию массива, и вы можете читать из любой позиции. Для этого он использует указатель, который содержит текущую позицию и предоставляет несколько методов, таких как seek()
для перемещения этого указателя. Как только вы окажетесь в правильном положении, вы можете получить FileChannel
из RandomAccessFile
и начать чтение данных из файла. Кстати, JDK 7 также представил NIO 2, что делает работу с файлами и каталогами еще проще. Прочитайте про Java 7 NIO.2 Ангела Леонарда, чтобы узнать больше о.
Как читать / записывать файлы, используя FileChannel и ByteBuffer
Прежде чем приступить к написанию кода, давайте пересмотрим базовую концепцию Channel и Buffer в Java NIO. Одним словом, буферы работают с каналом. Каналы — это труба, по которой передаются данные, а буферы являются источником и целью этой передачи данных. В случае записи данные, которые вы хотите записать, помещаются в буфер, который передается в канал, а затем канал считывает эти данные из буфера и записывает в файл.
Точно так же в случае чтения канал помещает данные в буфер, который вы предоставляете из файла , сети или любого другого источника. Поскольку один и тот же буфер используется для чтения и записи, т.е. вы записываете данные в буфер, но канал считывает их для записи в файл, вы должны вызвать метод flip () после завершения записи в буфер. Метод flip () изменяет указатели и позволяет читать данные из буфера. В Java существует три типа буфера: прямой, непрямой и сопоставленный буфер . В этом примере мы будем использовать прямой байтовый буфер.
Шаги для чтения / записи данных с использованием FileChannel и Buffer
Вот пошаговое руководство по началу чтения данных из файла с использованием RandomAccessFile , FileChannel и ByteBuffer :
- Откройте файл, который вы хотите прочитать / записать, используя RandomAccessFile в режиме чтения / записи.
- Вызовите метод
getChannel()
для RandomAccessFile, чтобы получить FileChannel. Положение возвращаемого канала всегда будет равно смещению указателя файла этого объекта, как возвращено методомgetFilePointer()
. - Создайте ByteBuffer с помощью
ByteBuffer.allocate()
. - Сохраните данные в ByteBuffer, используя различные методы
putInt()
, например,putInt()
,putLong()
. - Переверните буфер, чтобы канал мог читать данные из буфера и записывать в файл. Метод flip () изменяет указатели и позволяет читать данные из буфера.
- Вызовите метод write () FileChannel.
- Закройте FileChannel
- Закройте файл RandomAccessFile.
Еще один важный момент, на который следует обратить внимание, это то, что вы можете использовать один и тот же буфер для чтения и записи, но его нужно перевернуть Теперь давайте рассмотрим пример Java-программы для чтения / записи данных из файлов, используя FileChannel и ByteBuffer в Java. После Memory Mapped File это второй самый быстрый способ чтения и записи из файла в Java.
Java программа для чтения / записи из файла с использованием FileChannel и ByteBuffer
Вот пример программы, чтобы продемонстрировать, как вы можете читать и записывать данные из файла (может быть двоичного или текстового файла) с использованием классов FileChannel и ByteBuffer. Я также использовал абстракцию для создания интерфейса под названием Persistable, который предоставляет два метода persist()
и recover()
. Любой объект, который реализует этот интерфейс, может быть сохранен и загружен, но как вы сохраняете и загружаете их, оставлено на усмотрение разработчика, т.е. вы можете использовать Chanel и Buffer, как мы это делали, или вы можете использовать старый подход для чтения / записи файла в Java ,
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * Java Program to read and write on RandomAccessFile in Java * using FileChannle and ByteBuffer. * * @author Javin */ public class FileChannelDemo { public static void main(String args[]) { Tablet ipad = new Tablet( "Apple" , true , 1000 ); System.out.println( "Writing into RandomAcessFile : " + ipad); write( "tablet.store" , ipad); Tablet fromStore = new Tablet(); read( "tablet.store" , fromStore); System.out.println( "Object read from RandomAcessFile : " + fromStore); } /* * Method to write data into File using FileChannel and ByteBuffeer */ public static void write(String filename, Persistable object) { try { // Creating RandomAccessFile for writing RandomAccessFile store = new RandomAccessFile( "tablet" , "rw" ); // getting FileChannel from file FileChannel channel = store.getChannel(); // creating and initializing ByteBuffer for reading/writing data ByteBuffer buffer = ByteBuffer.allocate( 2048 ); // an instance of Persistable writing into ByteBuffer object.persist(buffer); // flip the buffer for writing into file buffer.flip(); int numOfBytesWritten = channel.write(buffer); // writing into File System.out.println( "number of bytes written : " + numOfBytesWritten); channel.close(); // closing file channel store.close(); // closing RandomAccess file } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /* * Method to read data from File using FileChannel and ByteBuffeer */ public static void read(String filename, Persistable object) { try { // Opening RandomAccessFile for reading data RandomAccessFile store = new RandomAccessFile( "tablet" , "rw" ); // getting file channel FileChannel channel = store.getChannel(); // preparing buffer to read data from file ByteBuffer buffer = ByteBuffer.allocate( 1024 ); // reading data from file channel into buffer int numOfBytesRead = channel.read(buffer); System.out.println( "number of bytes read : " + numOfBytesRead); // You need to filp the byte buffer before reading buffer.flip(); // Recovering object object.recover(buffer); channel.close(); store.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } |
Наш интерфейс для абстрактного механизма чтения и записи. Это также фактическое использование интерфейса для обеспечения абстракции, отделяющей то, что нужно сделать, от того, как это сделать. Подобно этому интерфейсу, просто скажите «сохранитесь и восстановитесь», а не как вы это делаете.
1
2
3
4
5
6
|
interface Persistable { public void persist(ByteBuffer buffer); public void recover(ByteBuffer buffer); } |
Конкретный класс для реализации Persistable, чтобы сделать их читаемыми и записываемыми:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
class Tablet implements Persistable { private String brand; private boolean isCellular; private long cost; // in US Dollars public Tablet() { brand = "" ; } public Tablet(String brand, boolean isCellular, long cost) { this .brand = brand; this .isCellular = isCellular; this .cost = cost; } public final String getBrand() { return brand; } public final boolean isCellular() { return isCellular; } public final long getCost() { return cost; } public final void setBrand(String brand) { this .brand = brand; } public final void setCellular( boolean isCellular) { this .isCellular = isCellular; } public final void setCost( long cost) { this .cost = cost; } @Override public void persist(ByteBuffer buffer) { byte [] strBytes = brand.getBytes(); buffer.putInt(strBytes.length); buffer.put(strBytes, 0 , strBytes.length); buffer.put(isCellular == true ? ( byte ) 1 : ( byte ) 0 ); buffer.putLong(cost); } @Override public void recover(ByteBuffer buffer) { int size = buffer.getInt(); byte [] rawBytes = new byte [size]; buffer.get(rawBytes, 0 , size); this .brand = new String(rawBytes); this .isCellular = buffer.get() == 1 ? true : false ; this .cost = buffer.getLong(); } @Override public String toString() { return "Tablet [brand=" + brand + ", isCellular=" + isCellular + ", cost=" + cost + "]" ; } } Output: Writing into RandomAcessFile : Tablet [brand=Apple, isCellular= true , cost= 1000 ] number of bytes written : 18 number of bytes read : 1024 Object read from RandomAcessFile : Tablet [brand=Apple, isCellular= true , cost= 1000 ] |
предосторожность
Не забудьте перевернуть байтовый буфер после записи в него содержимого объекта, потому что файловый канал должен прочитать его, чтобы записать данные в RandomAccessFile. Если вы забудете вызвать метод flip () перед вызовом FileChannel.write (), то в итоге вы ничего не напишите в файл.
Аналогично, после считывания данных из файла в буфер снова переверните его, чтобы вы могли прочитать данные из буфера в популярное содержимое объекта. Многие Java-программисты делают эту ошибку, не переворачивая после записи и заканчивая часами отладки, потому что либо ничего не записывается в файл, либо ничего не может прочитать из файла.
Это все о том, как читать / записывать файл, используя FileChannel и ByteBuffer в Java . В этом демоне я показал вам, как читать и писать файл RandomAccessFile, используя FileChannel и ByteBuffer, но вы можете применить ту же технику для чтения любого другого текстового или двоичного файла из программы Java.
Ссылка: | Чтение / запись в / из файлов с использованием FileChannel и ByteBuffer на Java от нашего партнера по JCG Джавина Пола из блога Javarevisited . |