Coming Soon

This lesson is currently being developed

File Basics

Read from and write to files.

I/O
Chapter
Beginner
Difficulty
25min
Estimated Time

What to Expect

Comprehensive explanations with practical examples

Interactive coding exercises to practice concepts

Knowledge quiz to test your understanding

Step-by-step guidance for beginners

Development Status

In Progress

Content is being carefully crafted to provide the best learning experience

Preview

Early Preview Content

This content is still being developed and may change before publication.

Basic file I/O

In this lesson, you'll learn how to work with files in C++ using file streams. You'll discover how to read from and write to files, handle different file modes, and implement proper error checking for file operations.

Introduction to file streams

File streams allow you to read from and write to files on disk using the same stream operations you've learned for console I/O. C++ provides three main file stream classes:

  • std::ifstream - Input file stream (reading from files)
  • std::ofstream - Output file stream (writing to files)
  • std::fstream - File stream (both reading and writing)
#include <iostream>
#include <fstream>

int main()
{
    std::ofstream outputFile("hello.txt");
    
    if (outputFile.is_open())
    {
        outputFile << "Hello, World!" << std::endl;
        outputFile << "This is written to a file." << std::endl;
        outputFile.close();
        
        std::cout << "File written successfully!" << std::endl;
    }
    else
    {
        std::cout << "Unable to open file!" << std::endl;
    }
    
    return 0;
}

Output:

File written successfully!

File contents (hello.txt):

Hello, World!
This is written to a file.

Writing to files with ofstream

Basic file writing

#include <iostream>
#include <fstream>
#include <string>

int main()
{
    std::ofstream outputFile("student_data.txt");
    
    if (!outputFile)
    {
        std::cerr << "Error: Cannot create file!" << std::endl;
        return 1;
    }
    
    // Write different types of data
    std::string name = "Alice Johnson";
    int age = 20;
    double gpa = 3.75;
    
    outputFile << "Student Information" << std::endl;
    outputFile << "==================" << std::endl;
    outputFile << "Name: " << name << std::endl;
    outputFile << "Age: " << age << std::endl;
    outputFile << "GPA: " << std::fixed << std::setprecision(2) << gpa << std::endl;
    
    outputFile.close();
    std::cout << "Student data written to file." << std::endl;
    
    return 0;
}

File contents (student_data.txt):

Student Information
==================
Name: Alice Johnson
Age: 20
GPA: 3.75

Writing multiple records

#include <iostream>
#include <fstream>
#include <vector>
#include <iomanip>

struct Student
{
    std::string name;
    int age;
    double gpa;
};

int main()
{
    std::vector<Student> students = {
        {"Alice Johnson", 20, 3.75},
        {"Bob Smith", 19, 3.92},
        {"Charlie Brown", 21, 3.58}
    };
    
    std::ofstream file("students.txt");
    
    if (!file)
    {
        std::cerr << "Error: Cannot create file!" << std::endl;
        return 1;
    }
    
    // Write header
    file << std::left << std::setw(15) << "Name" 
         << std::setw(5) << "Age" 
         << "GPA" << std::endl;
    file << std::string(25, '-') << std::endl;
    
    // Write student data
    for (const auto& student : students)
    {
        file << std::left << std::setw(15) << student.name
             << std::setw(5) << student.age
             << std::fixed << std::setprecision(2) << student.gpa << std::endl;
    }
    
    file.close();
    std::cout << "Student records written to file." << std::endl;
    
    return 0;
}

File contents (students.txt):

Name           Age  GPA
-------------------------
Alice Johnson  20   3.75
Bob Smith      19   3.92
Charlie Brown  21   3.58

Reading from files with ifstream

Basic file reading

#include <iostream>
#include <fstream>
#include <string>

int main()
{
    std::ifstream inputFile("student_data.txt");
    
    if (!inputFile)
    {
        std::cerr << "Error: Cannot open file!" << std::endl;
        return 1;
    }
    
    std::string line;
    std::cout << "File contents:" << std::endl;
    
    while (std::getline(inputFile, line))
    {
        std::cout << line << std::endl;
    }
    
    inputFile.close();
    
    return 0;
}

Output:

File contents:
Student Information
==================
Name: Alice Johnson
Age: 20
GPA: 3.75

Reading structured data

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>

struct Product
{
    std::string name;
    double price;
    int quantity;
};

std::vector<Product> readProductsFromCSV(const std::string& filename)
{
    std::vector<Product> products;
    std::ifstream file(filename);
    
    if (!file)
    {
        std::cerr << "Error: Cannot open file " << filename << std::endl;
        return products;
    }
    
    std::string line;
    
    // Skip header line
    std::getline(file, line);
    
    while (std::getline(file, line))
    {
        std::stringstream ss(line);
        std::string name, priceStr, quantityStr;
        
        if (std::getline(ss, name, ',') &&
            std::getline(ss, priceStr, ',') &&
            std::getline(ss, quantityStr, ','))
        {
            Product product;
            product.name = name;
            product.price = std::stod(priceStr);
            product.quantity = std::stoi(quantityStr);
            products.push_back(product);
        }
    }
    
    file.close();
    return products;
}

int main()
{
    // First, create a sample CSV file
    std::ofstream csvFile("products.csv");
    csvFile << "Name,Price,Quantity" << std::endl;
    csvFile << "Laptop,999.99,10" << std::endl;
    csvFile << "Mouse,25.50,50" << std::endl;
    csvFile << "Keyboard,75.00,30" << std::endl;
    csvFile.close();
    
    // Now read the products
    std::vector<Product> products = readProductsFromCSV("products.csv");
    
    std::cout << "Products loaded from file:" << std::endl;
    std::cout << std::left << std::setw(15) << "Name" 
              << std::setw(10) << "Price" 
              << "Quantity" << std::endl;
    std::cout << std::string(30, '-') << std::endl;
    
    for (const auto& product : products)
    {
        std::cout << std::left << std::setw(15) << product.name
                  << std::setw(10) << product.price
                  << product.quantity << std::endl;
    }
    
    return 0;
}

Output:

Products loaded from file:
Name           Price     Quantity
------------------------------
Laptop         999.99    10
Mouse          25.5      50
Keyboard       75        30

File opening modes

File streams can be opened in different modes to control how they behave:

#include <iostream>
#include <fstream>

int main()
{
    // Different ways to open files
    
    // 1. Default mode - truncate existing file
    std::ofstream file1("output.txt");
    file1 << "This overwrites existing content" << std::endl;
    file1.close();
    
    // 2. Append mode - add to end of existing file
    std::ofstream file2("output.txt", std::ios::app);
    file2 << "This is appended to the file" << std::endl;
    file2.close();
    
    // 3. Binary mode
    std::ofstream binaryFile("data.bin", std::ios::binary);
    int numbers[] = {1, 2, 3, 4, 5};
    binaryFile.write(reinterpret_cast<const char*>(numbers), sizeof(numbers));
    binaryFile.close();
    
    // 4. Read and write mode
    std::fstream readWriteFile("readwrite.txt", std::ios::in | std::ios::out | std::ios::trunc);
    readWriteFile << "Initial content" << std::endl;
    readWriteFile.seekg(0);  // Go to beginning
    
    std::string content;
    std::getline(readWriteFile, content);
    std::cout << "Read: " << content << std::endl;
    
    readWriteFile.close();
    
    return 0;
}

Output:

Read: Initial content

File contents (output.txt):

This overwrites existing content
This is appended to the file

File mode flags

#include <iostream>
#include <fstream>

void demonstrateFileModes()
{
    // ios::in - Open for reading
    std::ifstream inFile("input.txt", std::ios::in);
    
    // ios::out - Open for writing (default for ofstream)
    std::ofstream outFile("output.txt", std::ios::out);
    
    // ios::app - Append to end of file
    std::ofstream appendFile("log.txt", std::ios::app);
    
    // ios::trunc - Truncate file to zero length (default with ios::out)
    std::ofstream truncFile("temp.txt", std::ios::trunc);
    
    // ios::binary - Open in binary mode
    std::ofstream binFile("data.bin", std::ios::binary);
    
    // Combined modes
    std::fstream rwFile("data.txt", std::ios::in | std::ios::out | std::ios::app);
    
    // Close files
    inFile.close();
    outFile.close();
    appendFile.close();
    truncFile.close();
    binFile.close();
    rwFile.close();
}

int main()
{
    demonstrateFileModes();
    std::cout << "File modes demonstrated." << std::endl;
    return 0;
}

Error handling with file streams

Checking if file operations succeed

#include <iostream>
#include <fstream>

bool writeToFile(const std::string& filename, const std::string& content)
{
    std::ofstream file(filename);
    
    if (!file.is_open())
    {
        std::cerr << "Error: Cannot open file '" << filename << "' for writing." << std::endl;
        return false;
    }
    
    file << content;
    
    if (file.fail())
    {
        std::cerr << "Error: Failed to write to file '" << filename << "'." << std::endl;
        file.close();
        return false;
    }
    
    file.close();
    
    if (file.fail())
    {
        std::cerr << "Error: Failed to close file '" << filename << "'." << std::endl;
        return false;
    }
    
    return true;
}

std::string readFromFile(const std::string& filename, bool& success)
{
    std::ifstream file(filename);
    success = false;
    
    if (!file.is_open())
    {
        std::cerr << "Error: Cannot open file '" << filename << "' for reading." << std::endl;
        return "";
    }
    
    std::string content, line;
    while (std::getline(file, line))
    {
        content += line + "\n";
    }
    
    if (file.bad())
    {
        std::cerr << "Error: Failed to read from file '" << filename << "'." << std::endl;
        file.close();
        return "";
    }
    
    file.close();
    success = true;
    return content;
}

int main()
{
    std::string filename = "test.txt";
    std::string content = "Hello, file I/O!\nThis is a test.";
    
    // Write to file
    if (writeToFile(filename, content))
    {
        std::cout << "Successfully wrote to file." << std::endl;
        
        // Read from file
        bool success;
        std::string readContent = readFromFile(filename, success);
        
        if (success)
        {
            std::cout << "File contents:" << std::endl;
            std::cout << readContent << std::endl;
        }
    }
    
    return 0;
}

Output:

Successfully wrote to file.
File contents:
Hello, file I/O!
This is a test.

Working with binary files

Writing binary data

#include <iostream>
#include <fstream>

struct Person
{
    char name[50];
    int age;
    double salary;
};

int main()
{
    // Create some data
    Person people[] = {
        {"Alice Johnson", 28, 75000.50},
        {"Bob Smith", 32, 82000.00},
        {"Charlie Brown", 25, 68000.75}
    };
    
    // Write binary data
    std::ofstream binFile("people.dat", std::ios::binary);
    
    if (!binFile)
    {
        std::cerr << "Error: Cannot create binary file!" << std::endl;
        return 1;
    }
    
    // Write the number of records first
    int count = sizeof(people) / sizeof(Person);
    binFile.write(reinterpret_cast<const char*>(&count), sizeof(count));
    
    // Write the person records
    binFile.write(reinterpret_cast<const char*>(people), sizeof(people));
    
    binFile.close();
    
    std::cout << "Binary data written successfully." << std::endl;
    
    return 0;
}

Reading binary data

#include <iostream>
#include <fstream>
#include <iomanip>

struct Person
{
    char name[50];
    int age;
    double salary;
};

int main()
{
    std::ifstream binFile("people.dat", std::ios::binary);
    
    if (!binFile)
    {
        std::cerr << "Error: Cannot open binary file!" << std::endl;
        return 1;
    }
    
    // Read the number of records
    int count;
    binFile.read(reinterpret_cast<char*>(&count), sizeof(count));
    
    if (binFile.gcount() != sizeof(count))
    {
        std::cerr << "Error: Failed to read record count!" << std::endl;
        return 1;
    }
    
    std::cout << "Reading " << count << " records:" << std::endl;
    std::cout << std::left << std::setw(20) << "Name" 
              << std::setw(5) << "Age" 
              << "Salary" << std::endl;
    std::cout << std::string(35, '-') << std::endl;
    
    // Read each person record
    for (int i = 0; i < count; ++i)
    {
        Person person;
        binFile.read(reinterpret_cast<char*>(&person), sizeof(person));
        
        if (binFile.gcount() != sizeof(person))
        {
            std::cerr << "Error: Failed to read person record " << i << std::endl;
            break;
        }
        
        std::cout << std::left << std::setw(20) << person.name
                  << std::setw(5) << person.age
                  << std::fixed << std::setprecision(2) << person.salary << std::endl;
    }
    
    binFile.close();
    
    return 0;
}

Output:

Reading 3 records:
Name                Age  Salary
-----------------------------------
Alice Johnson       28   75000.50
Bob Smith           32   82000.00
Charlie Brown       25   68000.75

File utility functions

Checking if file exists

#include <iostream>
#include <fstream>

bool fileExists(const std::string& filename)
{
    std::ifstream file(filename);
    return file.good();
}

long getFileSize(const std::string& filename)
{
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    
    if (!file)
    {
        return -1;  // File doesn't exist or cannot be opened
    }
    
    return file.tellg();
}

int countLines(const std::string& filename)
{
    std::ifstream file(filename);
    
    if (!file)
    {
        return -1;  // File doesn't exist or cannot be opened
    }
    
    int lines = 0;
    std::string line;
    
    while (std::getline(file, line))
    {
        ++lines;
    }
    
    return lines;
}

int main()
{
    std::string filename = "test.txt";
    
    // Create a test file
    std::ofstream testFile(filename);
    testFile << "Line 1\n";
    testFile << "Line 2\n";
    testFile << "Line 3\n";
    testFile.close();
    
    // Test utility functions
    if (fileExists(filename))
    {
        std::cout << "File '" << filename << "' exists." << std::endl;
        std::cout << "File size: " << getFileSize(filename) << " bytes" << std::endl;
        std::cout << "Line count: " << countLines(filename) << " lines" << std::endl;
    }
    else
    {
        std::cout << "File '" << filename << "' does not exist." << std::endl;
    }
    
    return 0;
}

Output:

File 'test.txt' exists.
File size: 21 bytes
Line count: 3 lines

Practical file I/O examples

Simple logging system

#include <iostream>
#include <fstream>
#include <chrono>
#include <iomanip>

class Logger
{
private:
    std::ofstream logFile;
    
    std::string getCurrentTimestamp()
    {
        auto now = std::chrono::system_clock::now();
        auto time_t = std::chrono::system_clock::to_time_t(now);
        
        std::ostringstream oss;
        oss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
        return oss.str();
    }
    
public:
    Logger(const std::string& filename)
    {
        logFile.open(filename, std::ios::app);
        
        if (!logFile)
        {
            std::cerr << "Error: Cannot open log file!" << std::endl;
        }
    }
    
    ~Logger()
    {
        if (logFile.is_open())
        {
            logFile.close();
        }
    }
    
    void log(const std::string& level, const std::string& message)
    {
        if (logFile.is_open())
        {
            logFile << "[" << getCurrentTimestamp() << "] "
                   << "[" << level << "] "
                   << message << std::endl;
        }
    }
    
    void info(const std::string& message) { log("INFO", message); }
    void warning(const std::string& message) { log("WARNING", message); }
    void error(const std::string& message) { log("ERROR", message); }
};

int main()
{
    Logger logger("application.log");
    
    logger.info("Application started");
    logger.warning("This is a warning message");
    logger.error("This is an error message");
    logger.info("Application ended");
    
    std::cout << "Log entries written to application.log" << std::endl;
    
    return 0;
}

Configuration file handler

#include <iostream>
#include <fstream>
#include <map>
#include <sstream>

class ConfigManager
{
private:
    std::map<std::string, std::string> config;
    std::string filename;
    
public:
    ConfigManager(const std::string& configFile) : filename(configFile)
    {
        loadConfig();
    }
    
    bool loadConfig()
    {
        std::ifstream file(filename);
        
        if (!file)
        {
            std::cerr << "Warning: Cannot open config file. Using defaults." << std::endl;
            return false;
        }
        
        std::string line;
        
        while (std::getline(file, line))
        {
            // Skip empty lines and comments
            if (line.empty() || line[0] == '#')
                continue;
            
            size_t pos = line.find('=');
            if (pos != std::string::npos)
            {
                std::string key = line.substr(0, pos);
                std::string value = line.substr(pos + 1);
                
                // Trim whitespace (simplified)
                key.erase(key.find_last_not_of(" \t") + 1);
                value.erase(0, value.find_first_not_of(" \t"));
                
                config[key] = value;
            }
        }
        
        file.close();
        return true;
    }
    
    bool saveConfig()
    {
        std::ofstream file(filename);
        
        if (!file)
        {
            std::cerr << "Error: Cannot write to config file!" << std::endl;
            return false;
        }
        
        file << "# Application Configuration" << std::endl;
        
        for (const auto& pair : config)
        {
            file << pair.first << " = " << pair.second << std::endl;
        }
        
        file.close();
        return true;
    }
    
    void setString(const std::string& key, const std::string& value)
    {
        config[key] = value;
    }
    
    void setInt(const std::string& key, int value)
    {
        config[key] = std::to_string(value);
    }
    
    std::string getString(const std::string& key, const std::string& defaultValue = "")
    {
        auto it = config.find(key);
        return (it != config.end()) ? it->second : defaultValue;
    }
    
    int getInt(const std::string& key, int defaultValue = 0)
    {
        auto it = config.find(key);
        if (it != config.end())
        {
            try
            {
                return std::stoi(it->second);
            }
            catch (...)
            {
                return defaultValue;
            }
        }
        return defaultValue;
    }
    
    void printConfig()
    {
        std::cout << "Current configuration:" << std::endl;
        for (const auto& pair : config)
        {
            std::cout << pair.first << " = " << pair.second << std::endl;
        }
    }
};

int main()
{
    ConfigManager config("app.conf");
    
    // Set some configuration values
    config.setString("app_name", "My Application");
    config.setString("version", "1.0.0");
    config.setInt("max_connections", 100);
    config.setInt("timeout", 30);
    
    // Save configuration
    config.saveConfig();
    
    // Display current configuration
    config.printConfig();
    
    // Demonstrate reading values
    std::cout << "\nReading configuration:" << std::endl;
    std::cout << "App Name: " << config.getString("app_name") << std::endl;
    std::cout << "Max Connections: " << config.getInt("max_connections") << std::endl;
    
    return 0;
}

Best practices for file I/O

✅ Good practices:

#include <iostream>
#include <fstream>

int main()
{
    // Always check if file operations succeed
    std::ofstream file("output.txt");
    
    if (!file.is_open())
    {
        std::cerr << "Error: Cannot open file!" << std::endl;
        return 1;
    }
    
    // Use RAII - file will be automatically closed
    file << "Content written to file" << std::endl;
    
    // Check for write errors
    if (file.fail())
    {
        std::cerr << "Error: Write operation failed!" << std::endl;
        return 1;
    }
    
    // File automatically closed when going out of scope
    
    return 0;
}

❌ Common mistakes:

#include <iostream>
#include <fstream>

int main()
{
    // Bad: Not checking if file opened successfully
    std::ofstream file("output.txt");
    file << "This might not work!" << std::endl;  // Could fail silently
    
    // Bad: Not closing files (though destructors will do it)
    std::ifstream input("input.txt");
    // ... use file but never explicitly close
    
    // Bad: Not handling errors
    std::ifstream data("data.txt");
    int value;
    data >> value;  // What if this fails?
    std::cout << value << std::endl;  // Could be garbage
    
    return 0;
}

Summary

Basic file I/O in C++ involves:

  • File stream classes: std::ifstream, std::ofstream, and std::fstream
  • Opening files: Using constructors or the open() method
  • File modes: Controlling how files are opened (read, write, append, binary)
  • Error checking: Always verify that file operations succeed
  • Reading techniques: Line-by-line, structured data, and binary reading
  • Writing techniques: Text output, formatted data, and binary writing
  • File utilities: Checking existence, getting size, counting lines
  • RAII principle: Files are automatically closed when objects go out of scope

File I/O is essential for creating practical applications that can store and retrieve data persistently.

Quiz

  1. What's the difference between std::ifstream, std::ofstream, and std::fstream?
  2. How do you check if a file was opened successfully?
  3. What file mode would you use to add data to the end of an existing file?
  4. Why is it important to check the success of file operations?
  5. What happens to file handles when file stream objects go out of scope?

Practice exercises

Apply your file I/O knowledge with these exercises:

  1. Student Grade Manager: Create a program that can save student names and grades to a file, then read them back and calculate class statistics.

  2. Simple Database: Build a program that stores product information (name, price, quantity) in a file and provides functions to add, search, and update records.

  3. Log File Analyzer: Write a program that reads a log file and generates a summary report showing different types of log entries and their frequencies.

  4. Configuration Backup: Create a system that automatically backs up configuration files by copying them with timestamps in the filename.

Continue Learning

Explore other available lessons while this one is being prepared.

View Course

Explore More Courses

Discover other available courses while this lesson is being prepared.

Browse Courses

Lesson Discussion

Share your thoughts and questions

💬

No comments yet. Be the first to share your thoughts!

Sign in to join the discussion