Table of Contents
- Introduction
- BufferedReader Class
- BufferedWriter Class
- Constructors
- Key Methods
- Performance Benefits
- Examples and Use Cases
- Best Practices
- 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
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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
| Aspect | BufferedReader | FileReader |
|---|---|---|
| Performance | High (buffered) | Low (unbuffered) |
| Methods | readLine(), ready() | Basic read methods |
| Buffer Size | Configurable | No buffer |
| Use Case | Reading lines/text | Raw character reading |
BufferedWriter vs FileWriter
| Aspect | BufferedWriter | FileWriter |
|---|---|---|
| Performance | High (buffered) | Low (unbuffered) |
| Methods | newLine(), flush() | Basic write methods |
| Buffer Size | Configurable | No buffer |
| Use Case | Writing lines/text | Raw 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
- Use appropriate buffer sizes for your use case
- Prefer
readLine()for text processing over character-by-character reading - Use
try-with-resourcesfor automatic resource management - Consider memory usage when reading very large files
- 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.