NIO operation I. FileChannel

Definition

A new API is defined in JDK 1.4, i.e. New IO, which has different working modes from the standard IO API. The core components of NIO include channel, buffer and selectors.

Traditional IO is based on byte stream and character stream operation, which is blocking, while NIO is based on Channel and Buffer, which is non blocking. Selector is used to monitor events of multiple channels. A single thread can listen to multiple data channels.

Channel and Buffer

Channel is similar to the flow in IO, except that the flow is unidirectional, InputStream can only input but not output, OutputStream can only output but not input, and channel is powerful. It can use output as well as input, and be a father and a mother.

The main implementation of Channel is as follows:
FileChannel: data reading and writing of files
Datagram channel: data reading and writing of UDP
SocketChannel: data reading and writing of TCP, implemented by general client
ServerSocketChannel: generally, it is a server implementation.

FileChannel

FileChannel reading and writing depend on ByteBuffer. Call read to read the data into ByteBuffer, and call write to write the data in ByteBuffer back to the file.

Write a file, use ByteBuffer.wrap to package byte data as ByteBuffer, or call Charset class to encode a string and return ByteBuffer object.

private static void test0(String path) {
   try {
       FileOutputStream fileOutputStream = new FileOutputStream(path);
       FileChannel channel = fileOutputStream.getChannel();
       
       byte[] bytes ="abc Listen to the wind and night def".getBytes();
       ByteBuffer byteBuffer =ByteBuffer.wrap(bytes);
       channel.write(byteBuffer);
         channel.write(Charset.forName("utf-8").encode("abc Listen to the wind and night def"));
   }catch (IOException e){
        e.toString();
   }
}

To read a file, get FileChannel from FileInputStream. Otherwise, a java.nio.channels.NonReadableChannelException will be reported. Reading operation is more learned. First, define a cache area, use the read method to read to the cache area, and read() returns the read size. If - 1 is returned, it means that the read is playing. Get the read byte data through the array() method. Finally, clear the read data in the cache and prepare for the next read.

 try {
     FileInputStream fileInputStream = new FileInputStream(path);
     FileChannel channel = fileInputStream.getChannel();
     ByteBuffer byteBuffer =ByteBuffer.allocate(1024);
     while (channel.read(byteBuffer)>0){
         System.out.println(new String(byteBuffer.array()));
         byteBuffer.clear();
     }
 }catch (IOException e){
     e.toString();
 }

But this happens in the code above.

A lot of empty data will be output, because the ByteBuffer size is 1024, and the file size is 18 bytes, and there is no storage in the rest of 1024-18. The solution is very simple. It is to intercept the first N data in ByteBuffer.

private static void test1(String path) {
 try {
     FileInputStream fileInputStream = new FileInputStream(path);
     FileChannel channel = fileInputStream.getChannel();
     ByteBuffer byteBuffer =ByteBuffer.allocate(1024);
     int post=0;
     while (( post=channel.read(byteBuffer))>0){
         byte[] data =new byte[post];
         System.arraycopy(byteBuffer.array(),0,data,0,post);
         System.out.println(new String(data));
         byteBuffer.clear();
     }
 }catch (IOException e){
     e.toString();
 }
}

There is also a coquette operation. There is another knowledge in it, the flip() method and limit, which will be described below.

private static void test1(String path) {
    try {
     FileInputStream fileInputStream = new FileInputStream(path);
     FileChannel channel = fileInputStream.getChannel();
     ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
     while (channel.read(byteBuffer) > 0) {
         byteBuffer.flip();
         int limit = byteBuffer.limit();
         byte[] datas = new byte[limit];
         byteBuffer.get(datas);
         System.out.println(new String(datas));
         byteBuffer.clear();
     }
 } catch (IOException e) {
     e.toString();
 }

flip()

There are two modes of ByteBuffer, one is to write and the other is to read.

When writing, the internal pos attribute will move with the size of the write, which is equivalent to a pointer. The following code, with a byte put, the pos will move back 1, but when put int, it will move 4 bytes, because int is 4 bytes, and other types are the same, putLong will move 8 bytes. Internal limit=capacity. When the put size exceeds the limit, an error will be reported.

 ByteBuffer byteBuffer =ByteBuffer.allocate(100);
 System.out.println(byteBuffer);
 byteBuffer.put("1".getBytes());
 System.out.println(byteBuffer);
 byteBuffer.putInt(1);
  System.out.println(byteBuffer);
  byteBuffer.putLong(1);
  System.out.println(byteBuffer);
java.nio.HeapByteBuffer[pos=0 lim=100 cap=100]
java.nio.HeapByteBuffer[pos=1 lim=100 cap=100]
java.nio.HeapByteBuffer[pos=5 lim=100 cap=100]
java.nio.HeapByteBuffer[pos=13 lim=100 cap=100]

The flip method is used to switch reading. At this time, the limit will become the size after writing, and the pos is 0. Each time you get data, the post will move down. The next time you read from this location, an error will be reported until the excess limit is reached.

 ByteBuffer byteBuffer =ByteBuffer.allocate(100);
 System.out.println(byteBuffer);
 byteBuffer.put("1".getBytes());
 System.out.println(byteBuffer);
 byteBuffer.putInt(1);
  System.out.println(byteBuffer);
  byteBuffer.putLong(1);
  System.out.println(byteBuffer);
  byteBuffer.flip();
  System.out.println(byteBuffer);
java.nio.HeapByteBuffer[pos=0 lim=100 cap=100]
java.nio.HeapByteBuffer[pos=1 lim=100 cap=100]
java.nio.HeapByteBuffer[pos=5 lim=100 cap=100]
java.nio.HeapByteBuffer[pos=13 lim=100 cap=100]
java.nio.HeapByteBuffer[pos=0 lim=13 cap=100]

However, there is a common problem in the above code. In the face of Chinese, it may be disorderly code, although it does not appear above, as shown in the following example. Just turn the buffer down and the output will be scrambled. The reason is that the buffer size is 2, the data in the file is "ab c listening to the wind passing night def", the first reading is ab, and the second reading is half of c and listening words, so it's garbled. Some people say that it's useless to enlarge the cache. It can be seen from the calculation that, for example, the buffer size is 1024, a Chinese character has three bytes, and it can read < = 341 Chinese characters at a time, but 3 * 341 = 1023. If it is 342 Chinese characters at this time, it must read garbled code. Because the remaining byte is not enough to store a Chinese character, in fact, like FileInputStream direct read, there will be garbled code, which can be solved by FileReader or BufferedReader.

try {
    FileInputStream fileInputStream = new FileInputStream(path);
    FileChannel channel = fileInputStream.getChannel();
    ByteBuffer byteBuffer = ByteBuffer.allocate(2);
    while (channel.read(byteBuffer) > 0) {
        byteBuffer.flip();
        int limit = byteBuffer.limit();
        byte[] datas = new byte[limit];
        byteBuffer.get(datas);
        System.out.println(new String(datas));
        byteBuffer.clear();
    }
} catch (IOException e) {
    e.toString();
}


Online solutions to this problem are far more bizarre, few useful, but also in extreme cases will appear garbled code, or modify the buffer size to garbled code. Come on what I wrote, no matter how many characters the file has, or how big the buffer is, it's all inclusive!!!, the idea is simple.

 private static void test4(String path) {
     try {
         FileInputStream fileOutputStream = new FileInputStream(path);
         FileChannel channel = fileOutputStream.getChannel();
         List<byte[]> data = new ArrayList<>();
         ByteBuffer byteBuffer = ByteBuffer.allocate(1);
         int totalSize = 0;
         while (channel.read(byteBuffer) > 0) {
             byteBuffer.flip();
             byte[] soure = new byte[byteBuffer.limit()];
             System.arraycopy(byteBuffer.array(), 0, soure, 0, soure.length);
             totalSize += soure.length;
             data.add(soure);
             byteBuffer.clear();
         }
         byte[] bytesData = new byte[totalSize];
         int destPos = 0;
         for (int i = 0; i < data.size(); i++) {
             System.arraycopy(data.get(i), 0, bytesData, destPos, data.get(i).length);
             destPos += data.get(i).length;
         }
         System.out.println(new String(bytesData));
     } catch (
             IOException e) {
     }
 }

Buffer

Buffer has many data types. Of course, StringBuffer is the operation of Stirng.
The working state of Buffer is mentioned above, but there is a problem to explain. If the Buffer size is 10, put 5 data into it first, and switch to read mode. After reading 2 data, how many data stored in it will report an error?

The answer is three. When you switch to read mode, pos=0,limit=5. After you get twice, pos is 2. Then put does not report an error, but it will report when it exceeds the limit.

Other API s are simple and easy to understand.

Published 23 original articles, won praise 7, visited 6338
Private letter follow

Keywords: Java JDK REST Attribute

Added by jamiefoxx on Tue, 11 Feb 2020 13:46:18 +0200