Reading Files Line by Line in Java with BufferedReader and Scanner

Java, a versatile and widely-used programming language, offers multiple methods to read files line by line. In this guide, we will delve deep into two prominent methods: BufferedReader and Scanner. Both of these methods have their unique advantages and use-cases, making them indispensable tools for Java developers.

graph TD A[Java File Reading] --> B[BufferedReader] A --> C[Scanner] B --> D[Standard & Efficient] C --> E[Modern with Utility Methods]

BufferedReader: The Standard Approach

BufferedReader has been a part of Java since its early versions. It provides a straightforward and efficient way to read files line by line.

How BufferedReader Works

BufferedReader requires an InputStream, typically a FileInputStream, to read content. The readLine() method returns the content of a line or null if the end of the stream is reached.

Java
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;

public class BufferedReaderExample {  

    public static void main(String args[]) {
        FileInputStream fis = null;
        BufferedReader reader = null;

        try {
            fis = new FileInputStream("C:/sample.txt");
            reader = new BufferedReader(new InputStreamReader(fis));
            System.out.println("Reading File line by line using BufferedReader");

            String line = reader.readLine();
            while(line != null){
                System.out.println(line);
                line = reader.readLine();
            }

        } catch (FileNotFoundException ex) {
            Logger.getLogger(BufferedReaderExample.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(BufferedReaderExample.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                reader.close();
                fis.close();
            } catch (IOException ex) {
                Logger.getLogger(BufferedReaderExample.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

Note: Always ensure to close the FileInputStream or any IO Reader to release the associated file descriptor. Failing to do so may lead to resource leaks.

Scanner: A Modern Approach

Introduced in Java 1.5, Scanner has become a preferred choice for many developers, especially when reading inputs from the console. Its ability to read from InputStream means it can also read text files.

How Scanner Works

Scanner provides utility methods like nextLine() and nextInt(), making it a versatile tool for reading file contents.

Java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class ScannerExample {

    public static void main(String args[]) throws FileNotFoundException  {
        FileInputStream fis = new FileInputStream("C:/sample.txt");
        Scanner scanner = new Scanner(fis);

        System.out.println("Reading file line by line in Java using Scanner");

        while(scanner.hasNextLine()){
            System.out.println(scanner.nextLine());
        }

        scanner.close();
    }
}

Given the clean code and the variety of data conversion methods Scanner supports, it's a handy choice for reading files line by line in Java.

Choosing Between BufferedReader and Scanner

While both BufferedReader and Scanner are effective for reading files, your choice should depend on the specific requirements of your project. If you need a standard, efficient method, go with BufferedReader. However, if you require more utility methods and a modern approach, Scanner is the way to go.

Advanced Tips for Java Developers

For software engineers, full-stack developers, frontend developers, and all developer-related professions, understanding the intricacies of file reading in Java is crucial. Here are some advanced tips to enhance your file reading operations:

Efficient Buffering

While BufferedReader inherently uses buffering to read files, you can further optimize its performance by adjusting the buffer size. A larger buffer size can lead to faster reads for large files but may consume more memory.

Handling Large Files

For exceptionally large files, consider using memory-mapped file reading provided by the java.nio package. This method allows a file to be mapped to memory, enabling faster read operations.

Error Handling

Always implement robust error handling. For instance, when using BufferedReader or Scanner, ensure you handle FileNotFoundException and IOException appropriately. This not only prevents unexpected crashes but also enhances the user experience by providing meaningful error messages.

Character Encoding

Be mindful of character encoding, especially when dealing with international content. Both BufferedReader and Scanner allow you to specify character encoding. Ensuring the correct encoding prevents data corruption and ensures accurate file reading.

Closing Resources

Java 7 introduced the try-with-resources statement, which ensures that each resource is closed at the end of the statement. This is an efficient way to handle resource management and is highly recommended over manual closure.

Java
try (BufferedReader br = new BufferedReader(new FileReader("path-to-file"))) {
    // Your code here
} catch (IOException e) {
    e.printStackTrace();
}

Regular Expressions with Scanner

Scanner can utilize regular expressions, providing powerful parsing capabilities. This is especially useful when you need to extract specific data from a file.

Conclusion

Java offers multiple ways to read files line by line, with BufferedReader and Scanner being the most prominent. Depending on your project's needs, you can choose the method that best suits you. Always remember to close your streams to prevent resource leaks and ensure efficient performance.

Author