C++ でのファイル操作の実行

C++Beginner
オンラインで実践に進む

はじめに

この実験(Lab)では、C++ における様々なファイル操作を実行する方法を学びます。この実験では、読み書きのためのファイルを開くこと、ファイルへのテキストデータの書き込み、テキストファイルからのデータ読み込み、ファイルオープン状態の確認、ファイル読み書きエラーの処理、バイナリファイルの操作、ファイルポインタの位置決め、およびリソース解放のためのファイルのクローズについて扱います。C++ 標準ライブラリの基本的なファイルストリームクラスである ofstream および ifstream を使用してファイルと対話する実践的な経験を積むことができます。この実験の終わりまでに、C++ プログラミングにおけるファイルハンドリングについて確固たる理解を得られるでしょう。

ofstream および ifstream を使用したファイルを開く

このステップでは、C++ でファイルを開く方法を、2 つの基本的なファイルストリームクラス、すなわち書き込み用の ofstream と読み込み用の ifstream を使用して学習します。これらのクラスは C++ 標準ライブラリの一部であり、不可欠なファイルハンドリング機能を提供します。

まず、WebIDE ターミナルでプロジェクトディレクトリに移動します。

cd ~/project

file_operations.cpp という名前の新しい C++ ファイルを作成します。

touch file_operations.cpp

file_operations.cpp ファイルに次のコードを追加します。

#include <iostream>
#include <fstream>  // ファイルストリームヘッダをインクルード

int main() {
    // ofstream を使用して書き込み用にファイルを開く
    std::ofstream outputFile("example.txt");

    // ファイルが正常に開かれたか確認する
    if (outputFile.is_open()) {
        std::cout << "File opened for writing successfully!" << std::endl;
        outputFile.close();  // ファイルをクローズする
    } else {
        std::cout << "Unable to open file for writing." << std::endl;
    }

    // ifstream を使用して読み込み用にファイルを開く
    std::ifstream inputFile("example.txt");

    // ファイルが正常に開かれたか確認する
    if (inputFile.is_open()) {
        std::cout << "File opened for reading successfully!" << std::endl;
        inputFile.close();  // ファイルをクローズする
    } else {
        std::cout << "Unable to open file for reading." << std::endl;
    }

    return 0;
}

主要な概念を分解してみましょう。

  1. #include <fstream>: ファイルストリームライブラリをインクルードします
  2. std::ofstream: ファイル書き込み用の出力ファイルストリーム
  3. std::ifstream: ファイル読み込み用の入力ファイルストリーム
  4. is_open(): ファイルが正常に開かれたかどうかを確認します
  5. close(): 操作後にファイルをクローズします

プログラムをコンパイルします。

g++ file_operations.cpp -o file_operations

実行可能ファイルを実行します。

./file_operations

出力例:

File opened for writing successfully!
File opened for reading successfully!

ファイルストリームに関するいくつかの重要な点:

  • 操作を実行する前に、ファイルが正常に開かれたかどうかを必ず確認してください
  • 使用後はファイルをクローズすることを忘れないでください
  • ofstream はファイルへの書き込みに使用されます
  • ifstream はファイルからの読み込みに使用されます
  • どちらのクラスも <fstream> ヘッダの一部です

ファイルストリームは、ファイルへのドアを開けたり閉めたりするようなものだと考えるとよいでしょう。ofstream は部屋に書き込むことを許可し、ifstream は部屋の中身を読み取ることを許可します。

ファイルへのテキストデータの書き込み

このステップでは、C++ の ofstream クラスを使用してファイルにテキストデータを書き込む方法を学習します。前のステップを基に、文字列、数値、および複数行をファイルに書き込むさまざまな方法を探ります。

write_files.cpp という名前の新しい C++ ファイルを作成します。

touch ~/project/write_files.cpp

write_files.cpp ファイルに次のコードを追加します。

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

int main() {
    // 書き込み用にファイルを開く
    std::ofstream outputFile("student_data.txt");

    // ファイルが正常に開かれたか確認する
    if (outputFile.is_open()) {
        // 単一の文字列を書き込む
        outputFile << "John Doe" << std::endl;

        // 複数のデータを書き込む
        std::string name = "Alice Smith";
        int age = 22;
        double gpa = 3.75;
        outputFile << name << ", " << age << " years old, GPA: " << gpa << std::endl;

        // 複数行を書き込む
        outputFile << "Computer Science Student" << std::endl;
        outputFile << "University of Programming" << std::endl;

        // ファイルをクローズする
        outputFile.close();
        std::cout << "Data written to file successfully!" << std::endl;
    } else {
        std::cout << "Unable to open file for writing." << std::endl;
    }

    return 0;
}

プログラムをコンパイルします。

g++ write_files.cpp -o write_files

実行可能ファイルを実行します。

./write_files

出力例:

Data written to file successfully!

ファイルの内容を確認します。

cat student_data.txt

ファイル内容の例:

John Doe
Alice Smith, 22 years old, GPA: 3.75
Computer Science Student
University of Programming

ファイルへの書き込みに関する重要なポイント:

  • ファイルにデータを書き込むには << 演算子を使用します
  • std::endl は改行を追加します
  • 文字列、数値、変数を書き込むことができます
  • 書き込みを行う前にファイルが開いているか必ず確認してください
  • 書き込み操作の後にファイルをクローズしてください

ファイルへの書き込みは、ノートに書き込むようなものだと考えるとよいでしょう。ofstream がペンであり、ファイルはその情報が記録されるページです。

テキストファイルからのデータ読み取り

このステップでは、C++ の ifstream クラスを使用してテキストファイルからデータを読み込む方法を学習します。前のステップを基に、行、単語、ファイル全体を読み込むさまざまな方法を探ります。

read_files.cpp という名前の新しい C++ ファイルを作成します。

touch ~/project/read_files.cpp

read_files.cpp ファイルに次のコードを追加します。

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

int main() {
    // 前のステップで作成したファイルを開く
    std::ifstream inputFile("student_data.txt");

    // ファイルが正常に開かれたか確認する
    if (inputFile.is_open()) {
        // 行全体を読み込む
        std::string line;
        std::cout << "Reading entire lines:" << std::endl;
        while (std::getline(inputFile, line)) {
            std::cout << line << std::endl;
        }

        // ファイルポインタを先頭にリセットする
        inputFile.clear();
        inputFile.seekg(0, std::ios::beg);

        // 個々の単語を読み込む
        std::cout << "\nReading individual words:" << std::endl;
        std::string word;
        while (inputFile >> word) {
            std::cout << word << " ";
        }
        std::cout << std::endl;

        // ファイルをクローズする
        inputFile.close();
    } else {
        std::cout << "Unable to open file for reading." << std::endl;
    }

    return 0;
}

プログラムをコンパイルします。

g++ read_files.cpp -o read_files

実行可能ファイルを実行します。

./read_files

出力例:

Reading entire lines:
John Doe
Alice Smith, 22 years old, GPA: 3.75
Computer Science Student
University of Programming

Reading individual words:
John Doe Alice Smith, 22 years old, GPA: 3.75 Computer Science Student University of Programming

ファイル読み込みに関する重要なポイント:

  • 行全体を読み込むには std::getline() を使用します
  • 個々の単語を読み込むには >> 演算子を使用します
  • clear()seekg() はファイルポインタをリセットします
  • 読み込みを行う前にファイルが開いているか必ず確認してください
  • 読み込み操作の後にファイルをクローズしてください

ファイルからの読み込みは、本から情報を抽出するようなものだと考えるとよいでしょう。ifstream はブックマークのようなもので、テキスト内を移動するのに役立ちます。

ファイルオープン状態の確認

このステップでは、C++ でさまざまな方法を使用してファイル操作の状態を確認し、ファイルが正常に開かれて読み書きの準備ができているかを検証する方法を学習します。

file_status.cpp という名前の新しい C++ ファイルを作成します。

touch ~/project/file_status.cpp

file_status.cpp ファイルに次のコードを追加します。

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

int main() {
    // 既存のファイルを読み込み用に開こうと試みる
    std::ifstream inputFile("student_data.txt");

    // 方法 1: is_open() を使用してファイルの状態を確認する
    if (inputFile.is_open()) {
        std::cout << "File opened successfully for reading." << std::endl;

        // 読み込み操作を実行する
        std::string line;
        while (std::getline(inputFile, line)) {
            std::cout << "Read line: " << line << std::endl;
        }

        inputFile.close();
    } else {
        std::cout << "Failed to open file for reading." << std::endl;
    }

    // 方法 2: good() を使用してファイルの状態を確認する
    std::ofstream outputFile("test_status.txt");

    if (outputFile.good()) {
        std::cout << "File opened successfully for writing." << std::endl;
        outputFile << "Testing file status" << std::endl;

        // さらなる状態チェック
        if (outputFile) {
            std::cout << "File is in a good state for writing." << std::endl;
        }

        outputFile.close();
    } else {
        std::cout << "Failed to open file for writing." << std::endl;
    }

    // 方法 3: 複数の状態チェックメソッド
    std::ifstream checkFile("non_existent_file.txt");

    std::cout << "File status checks:" << std::endl;
    std::cout << "is_open(): " << (checkFile.is_open() ? "True" : "False") << std::endl;
    std::cout << "good(): " << (checkFile.good() ? "True" : "False") << std::endl;
    std::cout << "fail(): " << (checkFile.fail() ? "True" : "False") << std::endl;

    return 0;
}

プログラムをコンパイルします。

g++ file_status.cpp -o file_status

実行可能ファイルを実行します。

./file_status

出力例:

File opened successfully for reading.
Read line: John Doe
Read line: Alice Smith, 22 years old, GPA: 3.75
Read line: Computer Science Student
Read line: University of Programming
File opened successfully for writing.
File is in a good state for writing.
File status checks:
is_open(): False
good(): False
fail(): True

ファイル状態確認に関する重要なポイント:

  • is_open(): ファイルが正常に開かれたかどうかを確認します
  • good(): エラーが発生していないかどうかを確認します
  • fail(): エラーが発生したかどうかを確認します
  • 操作を実行する前に必ずファイルの状態を確認してください
  • さまざまなメソッドがファイルの状態を検証するための複数の方法を提供します

ファイル状態の確認は、旅に出る前の安全点検のようなものだと考えるとよいでしょう。これらのメソッドは、ファイル操作が安全かつ成功することを保証するのに役立ちます。

ファイル読み書きエラーの処理

このステップでは、C++ でのファイル読み書き操作中に発生する可能性のあるエラーを処理する方法を学習します。エラー処理を理解することは、堅牢なファイル操作プログラムを作成するために不可欠です。

file_error_handling.cpp という名前の新しい C++ ファイルを作成します。

touch ~/project/file_error_handling.cpp

file_error_handling.cpp ファイルに次のコードを追加します。

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

void writeToFile(const std::string& filename) {
    std::ofstream outputFile(filename);

    // ファイルが開かれているか確認する
    if (!outputFile) {
        // ファイルを開けない場合は例外をスローする
        throw std::runtime_error("Unable to open file for writing: " + filename);
    }

    try {
        // データの書き込みを試みる
        outputFile << "Hello, Error Handling!" << std::endl;
        outputFile << "This is a sample text." << std::endl;

        // 書き込みエラーをシミュレートする(意図的であり、推奨されない)
        if (outputFile.bad()) {
            throw std::runtime_error("Write operation failed");
        }
    }
    catch (const std::exception& e) {
        std::cerr << "Error during writing: " << e.what() << std::endl;
    }

    outputFile.close();
}

void readFromFile(const std::string& filename) {
    std::ifstream inputFile(filename);
    std::string line;

    // ファイルが開かれているか確認する
    if (!inputFile) {
        // ファイルを開けない場合は例外をスローする
        throw std::runtime_error("Unable to open file for reading: " + filename);
    }

    try {
        // ファイルの内容を読み取り、表示する
        std::cout << "File contents:" << std::endl;
        while (std::getline(inputFile, line)) {
            std::cout << line << std::endl;
        }

        // 読み取りエラーを確認する
        if (inputFile.bad()) {
            throw std::runtime_error("Read operation encountered an error");
        }
    }
    catch (const std::exception& e) {
        std::cerr << "Error during reading: " << e.what() << std::endl;
    }

    inputFile.close();
}

int main() {
    try {
        // ファイルへの書き込み
        writeToFile("error_handling_example.txt");

        // ファイルからの読み込み
        readFromFile("error_handling_example.txt");

        // 存在しないファイルからの読み込みを試みる
        readFromFile("non_existent_file.txt");
    }
    catch (const std::exception& e) {
        std::cerr << "Main error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

プログラムをコンパイルします。

g++ file_error_handling.cpp -o file_error_handling

実行可能ファイルを実行します。

./file_error_handling

出力例:

File contents:
Hello, Error Handling!
This is a sample text.
Error during reading: Unable to open file for reading: non_existent_file.txt

ファイルエラー処理に関する重要なポイント:

  • 潜在的な例外を処理するために try-catch ブロックを使用します
  • 操作の前後にファイルストリームの状態を確認します
  • 意味のあるエラーメッセージをスローするために std::runtime_error を使用します
  • さまざまな種類のファイル関連のエラーを処理します
  • 有益なエラーメッセージを提供します

エラー処理は、安全ネットのようなものだと考えるとよいでしょう。潜在的な問題を捕捉し、プログラムが予期せずクラッシュするのを防ぎます。

fstream を使用したバイナリファイルの操作

このステップでは、C++ の fstream クラスを使用してバイナリファイルを操作する方法を学習します。バイナリファイルは、データを生のバイナリ形式で格納し、テキストファイルとは異なり、構造化データを効率的に格納するのに役立ちます。

binary_files.cpp という名前の新しい C++ ファイルを作成します。

touch ~/project/binary_files.cpp

binary_files.cpp ファイルに次のコードを追加します。

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

// バイナリファイル書き込みを実演するためのシンプルな構造体
struct Student {
    int id;
    char name[50];
    double gpa;
};

void writeBinaryFile() {
    std::fstream binaryFile("students.bin", std::ios::out | std::ios::binary);

    if (!binaryFile) {
        std::cerr << "Error opening file for writing!" << std::endl;
        return;
    }

    // 学生レコードをいくつか作成する
    Student students[3] = {
        {1, "John Doe", 3.5},
        {2, "Alice Smith", 3.8},
        {3, "Bob Johnson", 3.2}
    };

    // 構造体全体をバイナリファイルに書き込む
    binaryFile.write(reinterpret_cast<char*>(students), sizeof(students));

    binaryFile.close();
    std::cout << "Binary file written successfully!" << std::endl;
}

void readBinaryFile() {
    std::fstream binaryFile("students.bin", std::ios::in | std::ios::binary);

    if (!binaryFile) {
        std::cerr << "Error opening file for reading!" << std::endl;
        return;
    }

    Student students[3];

    // 構造体全体をバイナリファイルから読み込む
    binaryFile.read(reinterpret_cast<char*>(students), sizeof(students));

    std::cout << "Student Records:" << std::endl;
    for (int i = 0; i < 3; ++i) {
        std::cout << "ID: " << students[i].id
                  << ", Name: " << students[i].name
                  << ", GPA: " << students[i].gpa << std::endl;
    }

    binaryFile.close();
}

int main() {
    // バイナリファイルの書き込み
    writeBinaryFile();

    // バイナリファイルの読み込み
    readBinaryFile();

    return 0;
}

プログラムをコンパイルします。

g++ binary_files.cpp -o binary_files

実行可能ファイルを実行します。

./binary_files

出力例:

Binary file written successfully!
Student Records:
ID: 1, Name: John Doe, GPA: 3.5
ID: 2, Name: Alice Smith, GPA: 3.8
ID: 3, Name: Bob Johnson, GPA: 3.2

バイナリファイルに関する重要なポイント:

  • バイナリモードには std::ios::binary フラグを使用します
  • バイナリデータには write() および read() メソッドを使用します
  • 型間の変換には reinterpret_cast が使用されます
  • 構造化データを効率的に格納するのに役立ちます
  • データの正確なバイナリ表現を保持します

バイナリファイルは、正確な設計図のようなものだと考えるとよいでしょう。メモリ上に存在するデータを、テキスト変換なしでそのまま格納します。

seekg/seekp によるファイルポインタの位置設定

このステップでは、C++ で読み取りには seekg()、書き込みには seekp() を使用してファイルポインタを操作する方法を学習します。これらのメソッドにより、ファイル内の特定の場所に移動したり、そこを変更したりすることができます。

file_pointer.cpp という名前の新しい C++ ファイルを作成します。

touch ~/project/file_pointer.cpp

file_pointer.cpp ファイルに次のコードを追加します。

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

void createSampleFile() {
    std::ofstream file("numbers.txt");
    for (int i = 1; i <= 10; ++i) {
        file << i << " ";
    }
    file.close();
}

void demonstrateSeekOperations() {
    // 読み取りおよび書き込みモードでファイルを開く
    std::fstream file("numbers.txt", std::ios::in | std::ios::out);

    if (!file) {
        std::cerr << "Error opening file!" << std::endl;
        return;
    }

    // 異なる位置からの読み取り
    std::cout << "Reading operations:" << std::endl;

    // 5 バイト目(文字位置)に移動する
    file.seekg(5);
    int value;
    file >> value;
    std::cout << "Value at 5th byte: " << value << std::endl;

    // ファイルの先頭に移動する
    file.seekg(0);
    file >> value;
    std::cout << "First value: " << value << std::endl;

    // 特定の位置への書き込み
    std::cout << "\nWriting operations:" << std::endl;

    // 特定の位置に移動して書き込む
    file.seekp(0);
    file << "100 ";

    // ファイルポインタをリセットし、検証のために読み取る
    file.seekg(0);
    file >> value;
    std::cout << "Modified first value: " << value << std::endl;

    file.close();
}

int main() {
    // 数値を含むサンプルファイルを作成する
    createSampleFile();

    // seek 操作を実演する
    demonstrateSeekOperations();

    return 0;
}

プログラムをコンパイルします。

g++ file_pointer.cpp -o file_pointer

実行可能ファイルを実行します。

./file_pointer

出力例:

Reading operations:
Value at 5th byte: 4
First value: 1

Writing operations:
Modified first value: 100

ファイルポインタに関する重要なポイント:

  • seekg(): 読み取りポインタ(get pointer)を移動します
  • seekp(): 書き込みポインタ(put pointer)を移動します
  • 最初の引数はバイト位置です
  • ファイル内でのランダムアクセスに役立ちます
  • 特定の場所に移動できます

ファイルポインタは、テキストエディタのカーソルのようなものだと考えるとよいでしょう。seekg()seekp() は、このカーソルを正確に目的の場所に移動させるのに役立ちます。

ファイルのクローズとリソースの解放

このステップでは、C++ でファイルを適切に閉じ、リソースを管理することの重要性について学習します。これにより、メモリリークを防ぎ、効率的なファイル処理を保証します。ファイルの閉じ方や、RAII (Resource Acquisition Is Initialization:リソース獲得は初期化) の原則の利用について探ります。

file_resources.cpp という名前の新しい C++ ファイルを作成します。

touch ~/project/file_resources.cpp

file_resources.cpp ファイルに次のコードを追加します。

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

// 手動でのファイルクローズ
void manualFileHandling() {
    std::ofstream outputFile("manual_file.txt");

    if (outputFile.is_open()) {
        outputFile << "Manually managed file resource" << std::endl;

        // 明示的にファイルを閉じる
        outputFile.close();

        std::cout << "File closed manually." << std::endl;
    }
}

// unique_ptr を使用した RAII ベースのファイル処理
void raii_fileHandling() {
    try {
        // ファイルリソースを管理するために unique_ptr を使用
        std::unique_ptr<std::ofstream> file(new std::ofstream("raii_file.txt"));

        if (file && file->is_open()) {
            *file << "RAII-managed file resource" << std::endl;
            std::cout << "RAII file handling successful." << std::endl;
        }
        // unique_ptr がスコープを抜けるとファイルは自動的に閉じられる
    }
    catch (const std::exception& e) {
        std::cerr << "Error in RAII file handling: " << e.what() << std::endl;
    }
}

// スコープベースのファイル処理
void scopeBasedFileHandling() {
    {
        // スコープを抜けるとファイルは自動的に閉じられる
        std::ofstream scopedFile("scoped_file.txt");

        if (scopedFile.is_open()) {
            scopedFile << "Scope-based file resource management" << std::endl;
            std::cout << "Scoped file handling successful." << std::endl;
        }
    } // ここでファイルが自動的に閉じられる
}

int main() {
    // さまざまなファイルリソース管理手法を実演する
    manualFileHandling();
    raii_fileHandling();
    scopeBasedFileHandling();

    return 0;
}

プログラムをコンパイルします。

g++ file_resources.cpp -o file_resources

実行可能ファイルを実行します。

./file_resources

出力例:

File closed manually.
RAII file handling successful.
Scoped file handling successful.

ファイルのクローズとリソース管理に関する重要なポイント:

  • 使用後は必ずファイルを閉じる
  • 手動で閉じる場合は close() メソッドを使用する
  • RAII の原則を活用する
  • スコープベースのリソース管理を使用する
  • リソースリークを防ぐ
  • ファイル操作中の例外を処理する

ファイルリソースは、図書館から本を借りるようなものだと考えるとよいでしょう。図書館(システムリソース)を整理整頓するために、使い終わったら必ず本(ファイル)を返却(クローズ)する必要があります。

まとめ

この実験(Lab)では、標準ライブラリの ofstream および ifstream クラスを使用して C++ でファイルをオープンする方法を学習しました。ファイルのオープン状態を確認するプロセス、ファイルへのテキストデータの書き込み、テキストファイルからのデータの読み取りについて探りました。さらに、ファイル読み書きエラーの処理、fstream を使用したバイナリファイルの操作、seekg/seekp によるファイルポインタの位置指定、そしてファイルの適切なクローズとリソースの解放について理解を深めました。これらの基本的なファイル操作は、C++ アプリケーションにおけるデータストレージと検索を管理するために不可欠です。