BufferedReader and BufferedWriter in Java

Table of Contents

  1. Introduction
  2. BufferedReader Class
  3. BufferedWriter Class
  4. Constructors
  5. Key Methods
  6. Performance Benefits
  7. Examples and Use Cases
  8. Best Practices
  9. Comparison with Other I/O Classes

Introduction

BufferedReader and BufferedWriter are Java I/O classes that provide buffering for character input and output streams. They wrap around other Reader and Writer objects to improve I/O performance by reducing the number of actual read/write operations.

BufferedReader Class

Purpose

BufferedReader reads text from a character-input stream, buffering characters to provide efficient reading of characters, arrays, and lines.

Key Features

  • Buffered reading for better performance
  • readLine() method for convenient line-by-line reading
  • Default buffer size of 8192 characters (can be customized)
  • Supports mark/reset functionality

BufferedWriter Class

Purpose

BufferedWriter writes text to a character-output stream, buffering characters to provide efficient writing of single characters, arrays, and strings.

Key Features

  • Buffered writing for better performance
  • newLine() method for platform-independent line separators
  • Default buffer size of 8192 characters (can be customized)
  • Automatic buffer flushing when buffer is full

Constructors

BufferedReader Constructors

// Creates a buffered character-input stream with default buffer size
BufferedReader(Reader in)
// Creates a buffered character-input stream with specified buffer size
BufferedReader(Reader in, int size)

BufferedWriter Constructors

// Creates a buffered character-output stream with default buffer size
BufferedWriter(Writer out)
// Creates a buffered character-output stream with specified buffer size
BufferedWriter(Writer out, int size)

Key Methods

BufferedReader Methods

MethodDescription
int read()Reads a single character
int read(char[] cbuf, int off, int len)Reads characters into a portion of an array
String readLine()Reads a line of text
long skip(long n)Skips characters
boolean ready()Tells whether this stream is ready to be read
void mark(int readAheadLimit)Marks the present position in the stream
void reset()Resets the stream to the marked position
void close()Closes the stream

BufferedWriter Methods

MethodDescription
void write(int c)Writes a single character
void write(char[] cbuf, int off, int len)Writes a portion of an array of characters
void write(String s, int off, int len)Writes a portion of a string
void newLine()Writes a line separator
void flush()Flushes the stream
void close()Closes the stream

Performance Benefits

Without Buffering (Inefficient)

FileReader reader = new FileReader("largefile.txt");
int character;
while ((character = reader.read()) != -1) {
// Each read() call results in a system call - SLOW!
process(character);
}
reader.close();

With Buffering (Efficient)

BufferedReader reader = new BufferedReader(new FileReader("largefile.txt"));
String line;
while ((line = reader.readLine()) != -1) {
// Fewer system calls - FAST!
process(line);
}
reader.close();

Examples and Use Cases

Example 1: Basic File Reading and Writing

import java.io.*;
public class BasicFileIO {
public static void main(String[] args) {
// Writing to a file
try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
writer.write("Hello, World!");
writer.newLine();
writer.write("This is a test file.");
writer.newLine();
writer.write("Goodbye!");
} catch (IOException e) {
e.printStackTrace();
}
// Reading from a file
try (BufferedReader reader = new BufferedReader(new FileReader("output.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

Example 2: Copying Text Files with Custom Buffer Size

import java.io.*;
public class FileCopy {
public static void copyFile(String sourcePath, String destPath) {
// Using custom buffer size (16KB instead of default 8KB)
try (BufferedReader reader = new BufferedReader(
new FileReader(sourcePath), 16384);
BufferedWriter writer = new BufferedWriter(
new FileWriter(destPath), 16384)) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine(); // Maintain line separators
}
System.out.println("File copied successfully!");
} catch (IOException e) {
System.err.println("Error copying file: " + e.getMessage());
}
}
public static void main(String[] args) {
copyFile("source.txt", "destination.txt");
}
}

Example 3: Reading Configuration Files

import java.io.*;
import java.util.*;
public class ConfigReader {
public static Map<String, String> readConfig(String configPath) {
Map<String, String> config = new HashMap<>();
try (BufferedReader reader = new BufferedReader(new FileReader(configPath))) {
String line;
int lineNumber = 0;
while ((line = reader.readLine()) != null) {
lineNumber++;
line = line.trim();
// Skip empty lines and comments
if (line.isEmpty() || line.startsWith("#")) {
continue;
}
// Parse key=value pairs
String[] parts = line.split("=", 2);
if (parts.length == 2) {
config.put(parts[0].trim(), parts[1].trim());
} else {
System.err.println("Invalid config format at line " + lineNumber);
}
}
} catch (IOException e) {
System.err.println("Error reading config file: " + e.getMessage());
}
return config;
}
public static void main(String[] args) {
Map<String, String> config = readConfig("config.properties");
config.forEach((key, value) -> System.out.println(key + " = " + value));
}
}

Example 4: Log File Processor

import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LogProcessor {
public static void processLogFile(String logPath, String outputPath) {
try (BufferedReader reader = new BufferedReader(new FileReader(logPath));
BufferedWriter writer = new BufferedWriter(new FileWriter(outputPath))) {
String line;
int errorCount = 0;
int warningCount = 0;
int infoCount = 0;
writer.write("LOG ANALYSIS REPORT");
writer.newLine();
writer.write("Generated at: " + LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
writer.newLine();
writer.newLine();
while ((line = reader.readLine()) != null) {
// Count log levels
if (line.contains("[ERROR]")) {
errorCount++;
writer.write("ERROR FOUND: " + line);
writer.newLine();
} else if (line.contains("[WARN]")) {
warningCount++;
} else if (line.contains("[INFO]")) {
infoCount++;
}
}
// Write summary
writer.newLine();
writer.write("SUMMARY:");
writer.newLine();
writer.write("Errors: " + errorCount);
writer.newLine();
writer.write("Warnings: " + warningCount);
writer.newLine();
writer.write("Info: " + infoCount);
writer.newLine();
writer.write("Total: " + (errorCount + warningCount + infoCount));
} catch (IOException e) {
System.err.println("Error processing log file: " + e.getMessage());
}
}
public static void main(String[] args) {
processLogFile("application.log", "log_report.txt");
}
}

Example 5: CSV File Reader with Data Validation

import java.io.*;
import java.util.*;
public class CSVReader {
public static List<Map<String, String>> readCSV(String csvPath) {
List<Map<String, String>> records = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(csvPath))) {
String headerLine = reader.readLine();
if (headerLine == null) {
System.err.println("Empty CSV file");
return records;
}
String[] headers = headerLine.split(",");
String line;
int rowNumber = 1;
while ((line = reader.readLine()) != null) {
rowNumber++;
String[] values = line.split(",");
if (values.length != headers.length) {
System.err.println("Skipping row " + rowNumber + 
": column count mismatch");
continue;
}
Map<String, String> record = new LinkedHashMap<>();
for (int i = 0; i < headers.length; i++) {
record.put(headers[i].trim(), values[i].trim());
}
records.add(record);
}
} catch (IOException e) {
System.err.println("Error reading CSV file: " + e.getMessage());
}
return records;
}
public static void main(String[] args) {
List<Map<String, String>> data = readCSV("data.csv");
data.forEach(record -> {
record.forEach((key, value) -> System.out.println(key + ": " + value));
System.out.println("---");
});
}
}

Example 6: Text File Search Utility

import java.io.*;
public class TextSearch {
public static void searchInFile(String filePath, String searchTerm, 
String outputPath) {
try (BufferedReader reader = new BufferedReader(new FileReader(filePath));
BufferedWriter writer = new BufferedWriter(new FileWriter(outputPath))) {
String line;
int lineNumber = 0;
int matchCount = 0;
writer.write("Search results for: " + searchTerm);
writer.newLine();
writer.write("File: " + filePath);
writer.newLine();
writer.newLine();
while ((line = reader.readLine()) != null) {
lineNumber++;
if (line.toLowerCase().contains(searchTerm.toLowerCase())) {
matchCount++;
writer.write("Line " + lineNumber + ": " + line);
writer.newLine();
}
}
writer.newLine();
writer.write("Total matches found: " + matchCount);
} catch (IOException e) {
System.err.println("Error searching file: " + e.getMessage());
}
}
public static void main(String[] args) {
searchInFile("large_text.txt", "java", "search_results.txt");
}
}

Example 7: Using with Try-With-Resources

import java.io.*;
import java.util.stream.Collectors;
public class ModernJavaIO {
// Java 8+ style using streams
public static List<String> readAllLines(String filePath) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
return reader.lines().collect(Collectors.toList());
}
}
// Reading large files in chunks
public static void processLargeFile(String inputPath, String outputPath) 
throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(inputPath));
BufferedWriter writer = new BufferedWriter(new FileWriter(outputPath))) {
char[] buffer = new char[8192]; // 8KB buffer
int charsRead;
while ((charsRead = reader.read(buffer)) != -1) {
// Process and write the chunk
writer.write(buffer, 0, charsRead);
}
}
}
// Appending to existing file
public static void appendToFile(String filePath, String content) 
throws IOException {
try (BufferedWriter writer = new BufferedWriter(
new FileWriter(filePath, true))) { // true for append mode
writer.write(content);
writer.newLine();
}
}
}

Best Practices

1. Always Use Try-With-Resources

// ✅ Good - automatic resource management
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
// Use reader
} catch (IOException e) {
// Handle exception
}
// ❌ Bad - manual resource management (prone to leaks)
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
// Use reader
} catch (IOException e) {
// Handle exception
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// Ignored
}
}
}

2. Choose Appropriate Buffer Size

// For large files, use larger buffer size
try (BufferedReader reader = new BufferedReader(
new FileReader("large_file.txt"), 32768)) { // 32KB buffer
// Process file
}
// For small files, default buffer size is usually sufficient
try (BufferedReader reader = new BufferedReader(new FileReader("small_file.txt"))) {
// Process file
}

3. Handle Character Encoding Properly

// Specify character encoding explicitly
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("file.txt"), "UTF-8"))) {
// Read with correct encoding
}
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("file.txt"), "UTF-8"))) {
// Write with correct encoding
}

4. Flush BufferedWriter When Needed

try (BufferedWriter writer = new BufferedWriter(new FileWriter("file.txt"))) {
writer.write("Important data");
writer.flush(); // Ensure data is written immediately
// Continue with more operations...
}

Comparison with Other I/O Classes

BufferedReader vs FileReader

AspectBufferedReaderFileReader
PerformanceHigh (buffered)Low (unbuffered)
MethodsreadLine(), ready()Basic read methods
Buffer SizeConfigurableNo buffer
Use CaseReading lines/textRaw character reading

BufferedWriter vs FileWriter

AspectBufferedWriterFileWriter
PerformanceHigh (buffered)Low (unbuffered)
MethodsnewLine(), flush()Basic write methods
Buffer SizeConfigurableNo buffer
Use CaseWriting lines/textRaw character writing

Common Pitfalls and Solutions

Pitfall 1: Forgetting to Close Resources

// ❌ Resource leak
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
String line = reader.readLine();
// Forgot to close reader!
// ✅ Proper resource management
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
String line = reader.readLine();
}

Pitfall 2: Ignoring Exceptions

// ❌ Silent failure
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
// operations
} catch (IOException e) {
// Empty catch block - bad practice!
}
// ✅ Proper exception handling
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
// operations
} catch (IOException e) {
logger.error("Failed to read file", e);
// Or rethrow, or take corrective action
}

Pitfall 3: Assuming Default Encoding

// ❌ Platform-dependent encoding
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
// May have encoding issues on different platforms
}
// ✅ Explicit encoding
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream("file.txt"), StandardCharsets.UTF_8))) {
// Consistent encoding across platforms
}

Performance Tips

  1. Use appropriate buffer sizes for your use case
  2. Prefer readLine() for text processing over character-by-character reading
  3. Use try-with-resources for automatic resource management
  4. Consider memory usage when reading very large files
  5. Use streaming approach for large files instead of loading everything into memory

BufferedReader and BufferedWriter are essential classes for efficient text file processing in Java, providing significant performance improvements over unbuffered I/O while offering convenient methods for common text processing tasks.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper