C++でのifstreamとofstreamを使ったファイルの読み書き方法完全ガイド

C++プログラミングにおいて、ファイル操作は基本的かつ重要なスキルです。本記事では、ifstreamとofstreamを使用したファイルの読み書き方法について、基本から応用までを丁寧に解説します。具体的なコード例やエラーハンドリングの方法も紹介し、実践的なファイル操作のスキルを身につけましょう。

目次

ifstreamとofstreamの基本概要

C++でファイルの読み込みと書き込みを行う際に使用されるのが、ifstreamとofstreamです。ifstreamは入力ファイルストリームで、ファイルからデータを読み込むために使用されます。ofstreamは出力ファイルストリームで、ファイルにデータを書き込むために使用されます。これらのストリームは、ヘッダーファイルをインクルードすることで利用可能になります。

ifstreamの基本構文

ifstreamの基本的な使用方法は以下の通りです:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream inputFile("example.txt");
    if (inputFile.is_open()) {
        // ファイルから読み込み処理
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

ofstreamの基本構文

ofstreamの基本的な使用方法は以下の通りです:

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outputFile("example.txt");
    if (outputFile.is_open()) {
        // ファイルへ書き込み処理
        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

ファイルを開く方法

ifstreamとofstreamを使用してファイルを開く方法について説明します。ファイルを開く際には、ファイル名とモードを指定します。

ifstreamでファイルを開く

ifstreamは入力ファイルストリームとして使用され、ファイルからデータを読み込むために用いられます。以下は基本的な構文です:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream inputFile("input.txt");
    if (!inputFile) {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
        return 1;
    }
    // ファイルの読み込み処理
    inputFile.close();
    return 0;
}

この例では、”input.txt”というファイルを開こうとしています。ファイルが存在しない場合、エラーメッセージを表示します。

ofstreamでファイルを開く

ofstreamは出力ファイルストリームとして使用され、ファイルにデータを書き込むために用いられます。以下は基本的な構文です:

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outputFile("output.txt");
    if (!outputFile) {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
        return 1;
    }
    // ファイルへの書き込み処理
    outputFile.close();
    return 0;
}

この例では、”output.txt”というファイルを開いて書き込みを行います。ファイルが開けない場合、エラーメッセージを表示します。

ファイルオープンモードの指定

ファイルを開く際に、追加のモードを指定することができます。例えば、既存のファイルに追加書き込みを行う場合は、std::ios::appモードを使用します:

std::ofstream outputFile("output.txt", std::ios::app);

このモードでは、既存のファイルの末尾にデータが追加されます。

ファイルへのデータ書き込み

ofstreamを使ってファイルにデータを書き込む方法について説明します。具体的なコード例を通じて、基本的な書き込み操作を理解しましょう。

基本的な書き込み操作

ofstreamを使用してファイルにデータを書き込む基本的な手順は以下の通りです:

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outputFile("output.txt");
    if (outputFile.is_open()) {
        outputFile << "Hello, World!" << std::endl;
        outputFile << "This is a test." << std::endl;
        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、”output.txt”というファイルを開いて、2行のテキストを書き込んでいます。outputFile.close()を使用して、書き込みが完了した後にファイルを閉じています。

複数行のデータ書き込み

複数行のデータを書き込む場合、ループを使用すると便利です:

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outputFile("output.txt");
    if (outputFile.is_open()) {
        for (int i = 1; i <= 5; ++i) {
            outputFile << "Line " << i << std::endl;
        }
        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、1行目から5行目までのテキストをファイルに書き込んでいます。

数値データの書き込み

テキストだけでなく、数値データも書き込むことができます:

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outputFile("output.txt");
    if (outputFile.is_open()) {
        int number = 42;
        double pi = 3.14159;
        outputFile << "Number: " << number << std::endl;
        outputFile << "Pi: " << pi << std::endl;
        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、整数と浮動小数点数をファイルに書き込んでいます。

ファイルからのデータ読み込み

ifstreamを使ってファイルからデータを読み込む方法について説明します。具体的なコード例を通じて、基本的な読み込み操作を理解しましょう。

基本的な読み込み操作

ifstreamを使用してファイルからデータを読み込む基本的な手順は以下の通りです:

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

int main() {
    std::ifstream inputFile("input.txt");
    if (inputFile.is_open()) {
        std::string line;
        while (std::getline(inputFile, line)) {
            std::cout << line << std::endl;
        }
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、”input.txt”というファイルを開いて、各行を読み込みながらコンソールに出力しています。

単語単位の読み込み

ファイルから単語単位でデータを読み込む場合は、>>演算子を使用します:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream inputFile("input.txt");
    if (inputFile.is_open()) {
        std::string word;
        while (inputFile >> word) {
            std::cout << word << std::endl;
        }
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、ファイルから単語ごとに読み込み、各単語をコンソールに出力しています。

数値データの読み込み

テキストだけでなく、数値データも読み込むことができます:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream inputFile("numbers.txt");
    if (inputFile.is_open()) {
        int number;
        while (inputFile >> number) {
            std::cout << "Number: " << number << std::endl;
        }
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、”numbers.txt”というファイルから整数を読み込み、各数値をコンソールに出力しています。

ファイルの終端に達したときの処理

ファイルの終端に達したときには、eof()メソッドを使用して確認します:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream inputFile("input.txt");
    if (inputFile.is_open()) {
        std::string line;
        while (!inputFile.eof()) {
            std::getline(inputFile, line);
            std::cout << line << std::endl;
        }
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、ファイルの終端に達するまで各行を読み込み、コンソールに出力しています。

ファイルのクローズ方法

ファイル操作が完了した後、ファイルを適切にクローズすることは非常に重要です。ファイルをクローズしないと、データの破損やメモリリークの原因となる可能性があります。

ファイルをクローズする理由

ファイルをクローズする理由は以下の通りです:

  • ファイルハンドルの解放:開かれたファイルはオペレーティングシステムによってリソースとして管理されます。クローズしないと、これらのリソースが解放されません。
  • データの整合性の確保:ファイルに書き込みを行った場合、クローズすることでバッファ内のデータが確実にディスクに書き込まれます。
  • メモリリークの防止:ファイルをクローズしないと、メモリリークの原因となることがあります。

ifstreamをクローズする方法

ifstreamでファイルを読み込んだ後、close()メソッドを使用してファイルをクローズします:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream inputFile("input.txt");
    if (inputFile.is_open()) {
        std::string line;
        while (std::getline(inputFile, line)) {
            std::cout << line << std::endl;
        }
        inputFile.close(); // ファイルをクローズ
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、ファイルの読み込みが完了した後、inputFile.close()を呼び出してファイルをクローズしています。

ofstreamをクローズする方法

ofstreamでファイルに書き込んだ後も、同様にclose()メソッドを使用してファイルをクローズします:

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outputFile("output.txt");
    if (outputFile.is_open()) {
        outputFile << "Hello, World!" << std::endl;
        outputFile.close(); // ファイルをクローズ
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、ファイルへの書き込みが完了した後、outputFile.close()を呼び出してファイルをクローズしています。

エラーハンドリング

ファイル操作中に発生する可能性のあるエラーを適切に処理することは、信頼性の高いプログラムを作成するために重要です。ここでは、ファイル操作中のエラーハンドリング方法について具体的に説明します。

ファイルが開けない場合のエラー処理

ファイルを開く際にエラーが発生した場合は、以下のようにエラーメッセージを表示することで対応できます:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream inputFile("nonexistent.txt");
    if (!inputFile) {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
        return 1;
    }
    inputFile.close();
    return 0;
}

この例では、存在しないファイルを開こうとした場合にエラーメッセージを表示します。

読み込みエラーの処理

ファイルからデータを読み込む際にエラーが発生した場合は、fail()メソッドを使用してエラーを検出できます:

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

int main() {
    std::ifstream inputFile("input.txt");
    if (inputFile.is_open()) {
        std::string line;
        while (std::getline(inputFile, line)) {
            if (inputFile.fail()) {
                std::cerr << "読み込みエラーが発生しました。" << std::endl;
                break;
            }
            std::cout << line << std::endl;
        }
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、読み込み中にエラーが発生した場合にエラーメッセージを表示します。

書き込みエラーの処理

ファイルにデータを書き込む際にエラーが発生した場合も、fail()メソッドを使用してエラーを検出できます:

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outputFile("output.txt");
    if (outputFile.is_open()) {
        outputFile << "Hello, World!" << std::endl;
        if (outputFile.fail()) {
            std::cerr << "書き込みエラーが発生しました。" << std::endl;
        }
        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、書き込み中にエラーが発生した場合にエラーメッセージを表示します。

例外を使ったエラーハンドリング

例外を使用することで、より高度なエラーハンドリングを行うことができます。以下は例外を使ったエラーハンドリングの例です:

#include <iostream>
#include <fstream>
#include <exception>

int main() {
    try {
        std::ifstream inputFile("input.txt");
        if (!inputFile) {
            throw std::runtime_error("ファイルを開けませんでした。");
        }
        std::string line;
        while (std::getline(inputFile, line)) {
            if (inputFile.fail()) {
                throw std::runtime_error("読み込みエラーが発生しました。");
            }
            std::cout << line << std::endl;
        }
        inputFile.close();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
    return 0;
}

この例では、ファイルのオープンや読み込みエラーが発生した場合に例外を投げてエラーメッセージを表示しています。

バイナリファイルの読み書き

バイナリファイルに対するifstreamとofstreamの使用方法について説明します。テキストファイルと異なり、バイナリファイルではデータをそのままの形式で読み書きするため、特別な処理が必要です。

バイナリファイルの書き込み

バイナリファイルにデータを書き込む場合、ofstreamをバイナリモードで開く必要があります。以下はその例です:

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outputFile("output.bin", std::ios::binary);
    if (outputFile.is_open()) {
        int number = 12345;
        outputFile.write(reinterpret_cast<char*>(&number), sizeof(number));
        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、整数値をバイナリ形式で”output.bin”に書き込んでいます。

バイナリファイルの読み込み

バイナリファイルからデータを読み込む場合、ifstreamをバイナリモードで開きます。以下はその例です:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream inputFile("output.bin", std::ios::binary);
    if (inputFile.is_open()) {
        int number;
        inputFile.read(reinterpret_cast<char*>(&number), sizeof(number));
        std::cout << "読み込んだ数値: " << number << std::endl;
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、”output.bin”から整数値をバイナリ形式で読み込み、コンソールに出力しています。

バイナリデータの配列の読み書き

配列形式のバイナリデータを扱うこともできます。以下にその例を示します:

バイナリデータの書き込み:

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outputFile("output_array.bin", std::ios::binary);
    if (outputFile.is_open()) {
        int numbers[] = {1, 2, 3, 4, 5};
        outputFile.write(reinterpret_cast<char*>(numbers), sizeof(numbers));
        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

バイナリデータの読み込み:

#include <iostream>
#include <fstream>

int main() {
    std::ifstream inputFile("output_array.bin", std::ios::binary);
    if (inputFile.is_open()) {
        int numbers[5];
        inputFile.read(reinterpret_cast<char*>(numbers), sizeof(numbers));
        for (int number : numbers) {
            std::cout << "読み込んだ数値: " << number << std::endl;
        }
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、整数の配列をバイナリ形式でファイルに書き込み、その後読み込んでコンソールに出力しています。

応用例:CSVファイルの読み書き

ifstreamとofstreamを使ってCSVファイルを読み書きする具体例を紹介します。CSVファイルは、データをカンマ区切りで保存するテキストファイルであり、多くのアプリケーションで使用されています。

CSVファイルの書き込み

まず、ofstreamを使用してCSVファイルにデータを書き込む方法を説明します:

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

int main() {
    std::ofstream outputFile("data.csv");
    if (outputFile.is_open()) {
        // ヘッダー行を書き込む
        outputFile << "Name,Age,Occupation" << std::endl;

        // データ行を書き込む
        outputFile << "Alice,30,Engineer" << std::endl;
        outputFile << "Bob,25,Designer" << std::endl;
        outputFile << "Charlie,35,Teacher" << std::endl;

        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、”data.csv”というファイルに3人分のデータを書き込んでいます。ヘッダー行も含めて書き込んでいます。

CSVファイルの読み込み

次に、ifstreamを使用してCSVファイルからデータを読み込む方法を説明します:

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

struct Person {
    std::string name;
    int age;
    std::string occupation;
};

int main() {
    std::ifstream inputFile("data.csv");
    if (inputFile.is_open()) {
        std::string line;
        std::vector<Person> people;

        // ヘッダー行をスキップ
        std::getline(inputFile, line);

        // データ行を読み込む
        while (std::getline(inputFile, line)) {
            std::stringstream ss(line);
            std::string name, ageStr, occupation;
            std::getline(ss, name, ',');
            std::getline(ss, ageStr, ',');
            std::getline(ss, occupation, ',');

            Person person;
            person.name = name;
            person.age = std::stoi(ageStr);
            person.occupation = occupation;
            people.push_back(person);
        }

        inputFile.close();

        // 読み込んだデータを出力する
        for (const Person& person : people) {
            std::cout << "Name: " << person.name
                      << ", Age: " << person.age
                      << ", Occupation: " << person.occupation
                      << std::endl;
        }
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、”data.csv”からデータを読み込み、Person構造体に格納して、コンソールに出力しています。ヘッダー行をスキップし、各行のデータをカンマで区切って読み込んでいます。

CSVファイルの読み書きの応用

CSVファイルの読み書きは、データのインポートやエクスポート、データベースとの連携など、さまざまな応用が可能です。以下は、CSVファイルを読み込み、特定の条件に基づいてフィルタリングする例です:

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

struct Person {
    std::string name;
    int age;
    std::string occupation;
};

int main() {
    std::ifstream inputFile("data.csv");
    std::ofstream outputFile("filtered_data.csv");
    if (inputFile.is_open() && outputFile.is_open()) {
        std::string line;
        std::vector<Person> people;

        // ヘッダー行を読み込み、書き出す
        std::getline(inputFile, line);
        outputFile << line << std::endl;

        // データ行を読み込む
        while (std::getline(inputFile, line)) {
            std::stringstream ss(line);
            std::string name, ageStr, occupation;
            std::getline(ss, name, ',');
            std::getline(ss, ageStr, ',');
            std::getline(ss, occupation, ',');

            Person person;
            person.name = name;
            person.age = std::stoi(ageStr);
            person.occupation = occupation;

            // 年齢が30以上の人をフィルタリングして書き出す
            if (person.age >= 30) {
                outputFile << person.name << "," << person.age << "," << person.occupation << std::endl;
            }
        }

        inputFile.close();
        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

この例では、”data.csv”からデータを読み込み、年齢が30以上の人のデータを”filtered_data.csv”に書き出しています。

演習問題:ファイル操作プログラムの作成

ここでは、これまで学んだifstreamとofstreamの知識を活用して、実際にファイル操作プログラムを作成するための演習問題を提供します。以下の課題に取り組んで、ファイル操作のスキルをさらに磨きましょう。

演習1:学生情報の読み書き

以下の仕様に従って、学生の情報を管理するプログラムを作成してください。

  1. students.csvファイルに学生の情報(名前、年齢、成績)を書き込みます。
  2. students.csvファイルから学生の情報を読み込み、コンソールに出力します。
  3. 学生の情報を構造体で管理します。

ヒント:

  • 学生の情報を格納する構造体を定義します。
  • 学生の情報を入力し、CSVファイルに書き込みます。
  • CSVファイルからデータを読み込み、構造体に格納します。

コード例:

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

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

int main() {
    std::vector<Student> students = {
        {"Alice", 20, 85.5},
        {"Bob", 22, 90.0},
        {"Charlie", 19, 78.2}
    };

    // 学生情報をファイルに書き込む
    std::ofstream outputFile("students.csv");
    if (outputFile.is_open()) {
        outputFile << "Name,Age,Grade" << std::endl;
        for (const Student& student : students) {
            outputFile << student.name << "," << student.age << "," << student.grade << std::endl;
        }
        outputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
        return 1;
    }

    // 学生情報をファイルから読み込む
    std::ifstream inputFile("students.csv");
    if (inputFile.is_open()) {
        std::string line;
        std::getline(inputFile, line); // ヘッダー行をスキップ

        while (std::getline(inputFile, line)) {
            std::stringstream ss(line);
            std::string name, ageStr, gradeStr;
            std::getline(ss, name, ',');
            std::getline(ss, ageStr, ',');
            std::getline(ss, gradeStr, ',');

            Student student;
            student.name = name;
            student.age = std::stoi(ageStr);
            student.grade = std::stod(gradeStr);
            std::cout << "Name: " << student.name
                      << ", Age: " << student.age
                      << ", Grade: " << student.grade << std::endl;
        }
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
        return 1;
    }

    return 0;
}

演習2:ログファイルの分析

以下の仕様に従って、ログファイルを分析するプログラムを作成してください。

  1. logs.txtファイルからログデータを読み込みます。
  2. ログのエントリを解析し、エラー(”ERROR”を含む行)をカウントします。
  3. エラー数をコンソールに出力します。

ヒント:

  • ログファイルの各行を読み込み、”ERROR”という文字列を含むかチェックします。
  • エラー行のカウントを行います。

コード例:

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

int main() {
    std::ifstream inputFile("logs.txt");
    int errorCount = 0;

    if (inputFile.is_open()) {
        std::string line;
        while (std::getline(inputFile, line)) {
            if (line.find("ERROR") != std::string::npos) {
                ++errorCount;
            }
        }
        inputFile.close();
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
        return 1;
    }

    std::cout << "Total ERROR entries: " << errorCount << std::endl;

    return 0;
}

まとめ

本記事では、C++におけるifstreamとofstreamを使用したファイルの読み書き方法について詳しく解説しました。基本的なファイルのオープン、データの読み書き、エラーハンドリングからバイナリファイルやCSVファイルの操作まで、多岐にわたる内容をカバーしました。実際のプログラム例や演習問題を通じて、実践的なスキルを身につけることができたでしょう。これらの知識を応用し、より高度なファイル操作プログラムを作成してみてください。

コメント

コメントする

目次