How do I read last n characters from a file?

In the following post you will learn how to read last n characters from a file. The JDK 7 introduces a new SeekableByteChannel interface which allows its implementation to change the position and the size of the byte channel. One of its implementation is the FileChannel class (java.nio.channels.FileChannel).

The FileChannel class make it possible to get hold the current position of where we are going to read from or write to a file. The code snippet below shows you how you can read the last 1000 characters from a log file.

package org.kodejava.io;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class FileReadLastNCharacters {
    public static void main(String[] args) {
        // Defines the path to the log file and creates a ByteBuffer.
        Path logPath = Paths.get("C:/tools/apache-tomcat-10.0.11/logs/catalina.out");
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        try {
            // Creates FileChannel and open the file channel for read access.
            FileChannel channel = FileChannel.open(logPath, StandardOpenOption.READ);

            // Read a sequence of bytes from the channel into the buffer starting
            // at given file position, which is the channel size - 1000. Because
            // we are going to read the last 1000 characters from the file.
            channel.read(buffer, channel.size() - 1000);
            System.out.println("Characters = " + new String(buffer.array()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

The steps in the code snippet above are:

  • Get the path to the log file.
  • Create a ByteBuffer, a buffer where the read bytes to be transferred.
  • Using the FileChannel.open() method we open a file to be read and return a FileChannel object.
  • The read() method of the FileChannel reads a sequence of bytes from the channel and transfer them to the given buffer starting and the position defined by channel.size() - 1000. This method returns the number of bytes read, possible zero, or -1 if the given position is greater than or equal to the file’s current size.
  • Print out the buffered string.

How do I read data from a buffer into channel?

In this example you’ll see how to read data from buffer using FileChannel.write() method call. Reading from a buffer means that you are writing data into the channel object. In the snippet below the data from our dummy buffer will be read and written into the result.txt file.

package org.kodejava.io;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class BufferRead {
    public static void main(String[] args) throws Exception {
        FileChannel channel = null;

        try {
            // Define an output file and create an instance of FileOutputStream
            File file = new File("result.txt");
            FileOutputStream fos = new FileOutputStream(file);

            // Create a dummy ByteBuffer which value to be read into a channel.
            ByteBuffer buffer = ByteBuffer.allocate(256);
            buffer.put(new byte[]{65, 66, 67, 68, 69, 70, 71, 72, 73, 74});

            // Change the buffer from writing mode to reading mode.
            buffer.flip();

            // Gets the channel from the FileOutputStream object and read the
            // data available in buffer using channel.write() method.
            channel = fos.getChannel();
            int bytesWritten = channel.write(buffer);
            System.out.println("written : " + bytesWritten);
        } finally {
            if (channel != null && channel.isOpen()) {
                channel.close();
            }
        }
    }
}

How do I use a FileChannel to read data into a Buffer?

The example below show how to use a FileChannel to read some data into a Buffer. We create a FileChannel from a FileInputStream instance. Because a channel reads data into buffer we need to create a ByteBuffer and set its capacity. Read data from a channel into buffer using the FileChannel.read() method.

To read out the data from the buffer we need to flip the buffer first using the Buffer.flip() method. The method will change the buffer from writing-mode into reading-mode. After the entire buffer is read clear the buffer using the clear() method call.

package org.kodejava.io;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class FileRead {
    public static void main(String[] args) {
        String path = "D:/Temp/source.txt";
        try (FileInputStream fis = new FileInputStream(path);
             FileChannel fileChannel = fis.getChannel()) {

            ByteBuffer buffer = ByteBuffer.allocate(64);

            int bytesRead = fileChannel.read(buffer);
            while (bytesRead != -1) {
                buffer.flip();

                while (buffer.hasRemaining()) {
                    System.out.print((char) buffer.get());
                }

                buffer.clear();
                bytesRead = fileChannel.read(buffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

How do I copy file using FileChannel class?

The example below show you how to copy a file using the java.nio.channels.FileChannel class.

package org.kodejava.io;

import java.io.*;
import java.nio.channels.FileChannel;

public class FileCopy {
    public static void main(String[] args) {
        //// Define the source and target file
        File source = new File("D:/Temp/source.txt");
        File target = new File("D:/Temp/target.txt");

        // Create the source and target channel
        try (FileChannel sourceChannel = new FileInputStream(source).getChannel();
             FileChannel targetChannel = new FileOutputStream(target).getChannel()) {

            // Copy data from the source channel into the target channel
            targetChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}