How do I use BufferedReader.lines() method to read file?

The BufferedReader.lines() method is a Java 8 method that returns a Stream, each element of which is a line read from the BufferedReader. This allows you to perform operations on each line with Java’s functional programming methods.

Returning a Stream of strings makes the BufferedReader.lines() method very efficient in terms of memory usage when working with large files. It reads the file line by line, instead of loading the entire file into memory at once.

Here is how it’s used to read from a file:

package org.kodejava.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class BufferedReaderLines {
    public static void main(String[] args) {
        Path path = Paths.get("README.MD");
        try (BufferedReader reader = Files.newBufferedReader(path)) {
            reader.lines().forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

This code opens a BufferedReader on the file located at the given path and uses the lines() method to get a Stream of lines from the file. Each line is then printed to the console using the System.out::println method reference.

The try-with-resources statement is there to ensure that the BufferedReader is closed after we’re done with it, even if an exception was thrown. The catch block is to handle a potential IOException which would be due to a file read error.

Bear in mind that not every situation requires or benefits from using streams, and in some cases, traditional processing methods might be more suitable. But when dealing with large datasets and when you wish to write declarative, clean, and efficient code, this method can be extremely useful.

How do I read a file line by line using Java NIO?

The java.nio.file.Files.lines() method is a Java NIO method used to read the contents of a file line by line. The code snippet will read the file from the specified filePath and print each line to the console. The Files.lines() method returns a Stream of strings, and we use Stream’s forEach method to print each line.

Note that we are using a try-with-resources statement which will automatically close the stream after we are done with it, it’s a good practice to always close streams to free-up system resources.

Here’s a basic example of how you can use it.

package org.kodejava.io;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
import java.util.stream.Stream;

public class ReadFile {
    public static void main(String[] args) {
        // replace with your file path
        String filePath = "/Users/wayan/lipsum.txt";

        // read file into stream, try-with-resources
        try (Stream<String> stream = Files.lines(Paths.get(filePath))) {

            stream.map(String::trim)
                    .forEach(System.out::println);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

The Files.lines() method, along with other Java I/O and NIO methods, provide several important benefits and features:

  1. Memory Efficiency: This method reads the file line by line lazily, which means it doesn’t load the entire content of the file into memory. This is particularly useful when dealing with large files that could potentially exhaust system memory.
  2. Stream API Integration: The method returns a Stream<String>, it can naturally integrate with Java Stream API. This allows you to take advantage of powerful functions provided by Stream API such as filtering, mapping, reduction, etc., to process the file data effectively.
  3. Readability: Using Files.lines() with a try-with-resources construct results in more compact and readable code compared to older methods such as BufferedReader. The try-with-resources statement ensures that each resource is closed at the end of the statement, which can simplify cleanup code and avoid resource leaks.
  4. Exceptions Handling: I/O operations can generally throw IOException which are checked Exceptions in Java. Using Files.lines() within a try-with-resources statement ensures that any underlying resources are closed properly, even in the event of an Exception.
  5. Parallel Processing: Since Files.lines() method returns a Stream<String>, you can convert this stream into a parallel stream if you want to process the file with multithreading.

Remember, like with many programming choices, whether to use Files.lines() or another method depends on the specific needs and constraints of your project.

How do I created tab delimited data file in Java?

The following code snippet show you how to create a tab delimited data file in Java. The tab character is represented using the \t sequence of characters, a backslash (\) character followed by the t letter. In the code below we start by defining some data that we are going to write to the file.

We create a PrintWriter object, passes a BufferedWritter created using the Files.newBufferedWriter() method. The countries.dat is the file name where the data will be written. Because we are using the try-with-resources the PrintWriter and the related object will be closed automatically when the file operation finishes.

package org.kodejava.io;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class TabDelimitedDataFile {
    public static void main(String[] args) throws IOException {
        List<String[]> data = new ArrayList<>();
        data.add(new String[]{"Afghanistan", "AF", "AFG", "004", "Asia"});
        data.add(new String[]{"Åland Islands", "AX", "ALA", "248", "Europe"});
        data.add(new String[]{"Albania", "AL", "ALB", "008", "Europe"});
        data.add(new String[]{"Algeria", "DZ", "DZA", "012", "Africa"});
        data.add(new String[]{"American Samoa", "AS", "ASM", "016", "Polynesia"});
        data.add(new String[]{"Andorra", "AD", "AND", "020", "South Europe"});
        data.add(new String[]{"Angola", "AO", "AGO", "024", "Africa"});
        data.add(new String[]{"Anguilla", "AI", "AIA", "660", "Americas"});
        data.add(new String[]{"Antarctica", "AQ", "ATA", "010", ""});
        data.add(new String[]{"Argentina", "AR", "ARG", "032", "Americas"});

        try (PrintWriter writer = new PrintWriter(
                Files.newBufferedWriter(Paths.get("countries.dat")))) {
            for (String[] row : data) {
                writer.printf("%1$20s\t%2$3s\t\t%3$3s\t\t%4$3s\t\t%5$s",
                        row[0], row[1], row[2], row[3], row[4]);
                writer.println();
            }
        }
    }
}

The output of the code snippet above are:

         Afghanistan     AF     AFG     004     Asia
       Åland Islands     AX     ALA     248     Europe
             Albania     AL     ALB     008     Europe
             Algeria     DZ     DZA     012     Africa
      American Samoa     AS     ASM     016     Polynesia
             Andorra     AD     AND     020     South Europe
              Angola     AO     AGO     024     Africa
            Anguilla     AI     AIA     660     Americas
          Antarctica     AQ     ATA     010     
           Argentina     AR     ARG     032     Americas

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 to monitor file or directory changes?

package org.kodejava.io;

import java.io.IOException;
import java.nio.file.*;

import static java.nio.file.StandardWatchEventKinds.*;

public class FileWatchDemo {
    public static void main(String[] args) {
        try {
            // Creates a instance of WatchService.
            WatchService watcher = FileSystems.getDefault().newWatchService();

            // Registers the logDir below with a watch service.
            Path logDir = Paths.get("F:/Temp/");
            logDir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);

            // Monitor the logDir at listen for change notification.
            while (true) {
                WatchKey key = watcher.take();
                for (WatchEvent<?> event : key.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();

                    if (ENTRY_CREATE.equals(kind)) {
                        System.out.println("Entry was created on log dir.");
                    } else if (ENTRY_MODIFY.equals(kind)) {
                        System.out.println("Entry was modified on log dir.");
                    } else if (ENTRY_DELETE.equals(kind)) {
                        System.out.println("Entry was deleted from log dir.");
                    }
                }
                key.reset();
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

To get the created, modified or deleted file you can see the following example: How to get the file name when using WatchService?.

How to get the file name when using WatchService?

package org.kodejava.io;

import java.io.IOException;
import java.nio.file.*;

import static java.nio.file.StandardWatchEventKinds.*;

public class WatchServiceGetFilename {
    public static void main(String[] args) {
        try {
            // Create a WatchService and register the logDir path with the
            // WatchService for ENTRY_CREATE.
            WatchService watcher = FileSystems.getDefault().newWatchService();
            Path logDir = Paths.get("F:/Temp");
            logDir.register(watcher, ENTRY_CREATE);

            while (true) {
                WatchKey key;
                try {
                    key = watcher.take();
                } catch (InterruptedException e) {
                    return;
                }

                for (WatchEvent<?> event : key.pollEvents()) {
                    if (event.kind() == ENTRY_CREATE) {
                        // Get the name of created file.
                        WatchEvent<Path> ev = cast(event);
                        Path filename = ev.context();

                        System.out.printf("A new file %s was created.%n",
                                filename.getFileName());
                    }
                }
                key.reset();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> WatchEvent<T> cast(WatchEvent<?> event) {
        return (WatchEvent<T>) event;
    }
}

How to read file using Files.newBufferedReader?

In the snippet below you’ll learn to open file for reading using Files.newBufferedReader() method in JDK 7. This method returns a java.io.BufferedReader which makes a backward compatibility with the old I/O system in Java.

To read a file you’ll need to provide a Path and the Charset to the newBufferedReader() method arguments.

package org.kodejava.io;

import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FilesNewBufferedReader {
    public static void main(String[] args) {
        Path logFile = Paths.get("app.log");
        try (BufferedReader reader =
                     Files.newBufferedReader(logFile, StandardCharsets.UTF_8)) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

How to write file using Files.newBufferedWriter?

To open a file for writing in JDK 7 you can use the Files.newBufferedWriter() method. This method takes three arguments. We need to pass the Path, the Charset and a varargs of OpenOption.

For example, in the snippet below we pass the path of our log file, we use the StandardCharsets.UTF_8 charset, and we use the StandardOpenOption.WRITE to open a file for writing. If you want to open a file and append its contents instead of rewriting it you can use the StandardOpenOption.APPEND.

package org.kodejava.io;

import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class FilesNewBufferedWriter {
    public static void main(String[] args) throws Exception {
        Path logFile = Paths.get("app.log");
        if (Files.notExists(logFile)) {
            Files.createFile(logFile);
        }

        try (BufferedWriter writer =
                     Files.newBufferedWriter(logFile, StandardCharsets.UTF_8,
                             StandardOpenOption.WRITE)) {

            for (int i = 0; i < 10; i++) {
                writer.write(String.format("Message %s%n", i));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Because we use the StandardOpenOption.WRITE we have to make sure that the file to be written is exists. If the file is not available we will get error like java.nio.file.NoSuchFileException.

How do I move a file in JDK 7?

In the following code snippet you will learn how to move a file using the java.nio.file.Files helper class of JDK 7. This class simplify how you can move file. To move file you need to define the Path of the source and the target file.

We use the Files.move() method to move the file by passing the source and target path. We can also define the CopyOptions of the move process. For example to tell the move operation to replace the target file if the file already exist we can use the StandardCopyOption.REPLACE_EXISTING option. This option is a varargs, that means we can pass multiple options.

package org.kodejava.io;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static java.nio.file.StandardCopyOption.*;

public class FileMoveDemo {
    public static void main(String[] args) {
        // Define the source and target of the file to be moved.
        Path source = Paths.get("F:/Temp/data.txt");
        Path target = Paths.get("F:/Temp/data.bak");

        try {
            // Move file from source to target using the defined
            // configuration (REPLACE_EXISTING)
            Files.move(source, target, REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

How do I find files in a directory using DirectoryStream?

In this example we are going to learn to use the DirectoryStream, which part of java.nio.file package, to find files in a directory. We begin by creating a Path, the directory where the search will be conducted.

After that we create a DirectoryStream using Files.newDirectoryStream(). To create a directory stream we passed two arguments, the starting path and a glob expression. Below we use *.txt glob expression to filter all text files in the F:/Temp.

To get all the entries of the directory stream we can use a foreach loop as can be seen below. We iterate each entry, which is a Path object. And we print the entries file name using the getFileName() method.

package org.kodejava.io;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FindFilesInDirectory {
    public static void main(String[] args) {
        Path dir = Paths.get("F:/Temp");

        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.txt")) {
            for (Path entry : stream) {
                System.out.println(entry.getFileName());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

You can also notice that in this example we are using the try-with-resource statement. This mean that the DirectoryStream will be closed appropriately when the process is done. This type of try statement is an improvement introduced in JDK 7.