File Handling

Lesson 0018 — 40 min read

File Handling refers to the process of reading from and writing to files to store and retrieve data persistently on disk. It is vital in object-oriented software for application logging, configuration management, and user data persistence.

Core Operations

1. Checking File Properties and Metadata

Before attempting I/O operations, modern object-oriented code inspects paths and verifies file properties.

import java.io.File;

public class Main {
    public static void main(String[] args) {
        File file = new File("example.txt");

        if (file.exists()) {
            System.out.println("File Name: " + file.getName());
            System.out.println("Absolute Path: " + file.getAbsolutePath());
            System.out.println("Writable: " + file.canWrite());
            System.out.println("Readable: " + file.canRead());
            System.out.println("File Size in bytes: " + file.length());
        } else {
            System.out.println("The file does not exist.");
        }
    }
}
#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

int main() {
    fs::path p("example.txt");

    if (fs::exists(p)) {
        cout << "File Name: " << p.filename().string() << "\n";
        cout << "Absolute Path: " << fs::absolute(p).string() << "\n";
        cout << "File Size in bytes: " << fs::file_size(p) << "\n";
    } else {
        cout << "The file does not exist.\n";
    }
    return 0;
}
from pathlib import Path

def main():
    p = Path("example.txt")

    if p.exists():
        print("File Name:", p.name)
        print("Absolute Path:", str(p.resolve()))
        print("File Size in bytes:", p.stat().st_size)
    else:
        print("The file does not exist.")

if __name__ == "__main__":
    main()

2. Writing Data to a File

Writing data converts application strings into character streams on disk. Buffering is used to group small write requests for better disk performance.

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
            writer.write("Hello, world!\n");
            writer.write("This is a sample file.");
            System.out.println("File written successfully.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ofstream writer("output.txt");

    if (!writer.is_open()) {
        cout << "Failed to open output.txt\n";
        return 0;
    }

    writer << "Hello, world!\n";
    writer << "This is a sample file.\n";
    writer.close();

    cout << "File written successfully.\n";
    return 0;
}
def main():
    try:
        with open("output.txt", "w") as writer:
            writer.write("Hello, world!\n")
            writer.write("This is a sample file.\n")
        print("File written successfully.")
    except Exception:
        print("Failed to write the file.")

if __name__ == "__main__":
    main()

3. Reading Data Line-by-Line

Iterating line-by-line is memory efficient, allowing applications to process files much larger than available RAM.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
            String line;
            int i = 1;
            while ((line = reader.readLine()) != null) {
                System.out.println("Line " + i + ": " + line);
                i++;
            }
        } catch (IOException e) {
            System.out.println("Error reading file.");
        }
    }
}
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
    ifstream reader("example.txt");

    if (!reader.is_open()) {
        cout << "Failed to open example.txt\n";
        return 0;
    }

    string line;
    int i = 1;
    while (getline(reader, line)) {
        cout << "Line " << i << ": " << line << "\n";
        i++;
    }
    reader.close();
    return 0;
}
def main():
    try:
        with open("example.txt", "r") as reader:
            i = 1
            for line in reader:
                print(f"Line {i}: {line.strip()}")
                i += 1
    except FileNotFoundError:
        print("The file does not exist.")

if __name__ == "__main__":
    main()

4. Automatic Resource Cleanup

File handles are limited system resources. Forgetting to close streams causes file locks and memory leaks.

5. Real-World Application: Building a Custom Logger

Logging uses append mode so new messages are added to the audit file without wiping prior entries.

import java.io.*;

class Logger {
    private String path;

    public Logger(String path) throws IOException {
        this.path = path;
        File file = new File(path);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    public void log(String message) {
        // FileWriter with true parameter enables append mode
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(path, true))) {
            bw.write(message);
            bw.newLine();
        } catch (Exception e) {
            System.out.println("Failed to log: " + message);
        }
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Logger logger = new Logger("application.log");
        logger.log("Application started...");
        logger.log("User logged in.");
        logger.log("Application closed.");
    }
}
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

class Logger {
private:
    string path;
public:
    Logger(const string& path) : path(path) {
        ofstream tmp(path, ios::app);
        tmp.close();
    }

    void log(const string& message) {
        // ios::app enables append mode
        ofstream out(path, ios::app);
        if (!out.is_open()) return;

        out << message << "\n";
        out.close();
    }
};

int main() {
    Logger logger("application.log");
    logger.log("Application started...");
    logger.log("User logged in.");
    logger.log("Application closed.");
    return 0;
}
class Logger:
    def __init__(self, path):
        self.path = path
        # Ensure file exists
        with open(self.path, "a"):
            pass

    def log(self, message):
        try:
            # Mode 'a' enables append mode
            with open(self.path, "a") as writer:
                writer.write(message + "\n")
        except Exception:
            print("Failed to log:", message)

def main():
    logger = Logger("application.log")
    logger.log("Application started...")
    logger.log("User logged in.")
    logger.log("Application closed...")

if __name__ == "__main__":
    main()

Comparison Summary

Feature Java C++ Python
File Metadata API java.io.File std::filesystem::path (C++17) pathlib.Path / os module
Buffered Write API BufferedWriter(new FileWriter(path)) std::ofstream writer(path) open(path, "w") (built-in buffering)
Line Reader API BufferedReader(new FileReader(path)) std::ifstream with getline() Line iteration over open(path, "r")
Auto Resource Closing try-with-resources RAII (destructors auto-close on scope exit) with statement (Context Manager)
Append Mode new FileWriter(path, true) ofstream(path, ios::app) open(path, "a")

What is the main purpose of Java's try-with-resources statement in File Handling?

Which mode should be used when opening a file to add logs without overwriting existing data?

Notes

File Handling :-

Definition ⇒ Reading/writing files for persistent data storage on disk

Modes ⇒ Read (r / ifstream) ; Write (w / ofstream) ; Append (a / ios::app)

Resource Management :-

Java ⇒ try-with-resources auto-closes AutoCloseable streams

C++ ⇒ RAII destructors auto-close ifstream / ofstream handles on scope exit

Python ⇒ with open(...) context manager auto-closes files on exit

Metadata Inspection :-

Java ⇒ File class (exists(), length(), getAbsolutePath())

C++ ⇒ C++17 std::filesystem (exists(), file_size(), absolute())

Python ⇒ pathlib.Path (exists(), stat().st_size, resolve())

Primary source: Oracle Java Tutorials — Basic I/O, cppreference — fstream & Python Docs — Reading and Writing Files. Ask me anything that's unclear.

← Prev: Generics & Templates Next: SOLID Principles →

Questions? Ask your agent — you can follow up on any concept, quiz answer, or how this applies to a specific coding problem. Review the Java glossary or syntax quick reference for a compressed overview.