C++によるファイル入出力とディレクトリ操作の完全ガイド

C++を使用してファイルの読み書きやディレクトリの操作を行うことは、多くのプログラムで必要となる基本的なスキルです。本記事では、C++を用いたファイル入出力とディレクトリ操作の基本から応用まで、具体的なコード例を交えて詳しく解説します。

目次
  1. C++でのファイル入出力の基本
    1. ファイルを開く
    2. ファイルの読み込み
    3. ファイルへの書き込み
  2. テキストファイルの読み込みと書き込み
    1. テキストファイルの読み込み
    2. テキストファイルへの書き込み
    3. 追加書き込み
  3. バイナリファイルの読み込みと書き込み
    1. バイナリファイルの読み込み
    2. バイナリファイルへの書き込み
    3. バイナリデータの操作例
  4. ディレクトリの作成方法
    1. 標準ライブラリを使用する
    2. プラットフォーム固有のAPIを使用する
  5. ディレクトリの削除方法
    1. 標準ライブラリを使用する
    2. プラットフォーム固有のAPIを使用する
    3. ディレクトリ削除の注意点
  6. ディレクトリの移動方法
    1. 標準ライブラリを使用する
    2. プラットフォーム固有のAPIを使用する
    3. ディレクトリ移動の注意点
  7. エラーハンドリング
    1. ファイル操作のエラーハンドリング
    2. ディレクトリ操作のエラーハンドリング
    3. 例外を用いたエラーハンドリング
    4. ディレクトリ操作における例外処理
  8. 実践例: ファイルコピー機能の実装
    1. 標準ライブラリを使用したファイルコピー
    2. 低レベルAPIを使用したファイルコピー
    3. ファイルコピーの注意点
  9. 応用: ファイル検索ツールの作成
    1. 標準ライブラリを使用したファイル検索
    2. 特定の条件に一致するファイルの検索
    3. 正規表現を使用したファイル名の検索
    4. ファイル検索ツールの実用例
  10. まとめ

C++でのファイル入出力の基本

C++では、ファイルの読み書きに「fstream」ライブラリを使用します。このライブラリには、ファイルを読み込むための「ifstream」、書き込むための「ofstream」、そして読み書きの両方ができる「fstream」が含まれています。以下に、これらの基本的な使い方を紹介します。

ファイルを開く

ファイルを開くためには、まず「ifstream」や「ofstream」のインスタンスを作成し、そのコンストラクタにファイル名を渡します。例えば、以下のコードでは「data.txt」というファイルを読み込み用に開いています。

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ifstream inputFile("data.txt");
    if (!inputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }
    // ファイル処理
    inputFile.close();
    return 0;
}

ファイルの読み込み

ファイルからデータを読み込むためには、「ifstream」の「>>」演算子や「getline」関数を使用します。以下の例では、ファイルから1行ずつ読み込んで表示しています。

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
    ifstream inputFile("data.txt");
    if (!inputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }

    string line;
    while (getline(inputFile, line)) {
        cout << line << endl;
    }

    inputFile.close();
    return 0;
}

ファイルへの書き込み

ファイルにデータを書き込むためには、「ofstream」を使用し、「<<」演算子でデータを出力します。以下の例では、「output.txt」というファイルにテキストを書き込んでいます。

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ofstream outputFile("output.txt");
    if (!outputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }

    outputFile << "こんにちは、世界!" << endl;

    outputFile.close();
    return 0;
}

これらの基本操作を理解することで、C++におけるファイル入出力の基礎を習得できます。次のセクションでは、テキストファイルの読み込みと書き込みについて詳しく見ていきます。

テキストファイルの読み込みと書き込み

C++では、テキストファイルの読み書きを通じてデータの保存や取得を行うことができます。このセクションでは、テキストファイルの具体的な読み込みと書き込み方法について詳しく説明します。

テキストファイルの読み込み

テキストファイルを読み込む際には、「ifstream」を使用します。以下のコード例では、ファイル「example.txt」から1行ずつテキストを読み込んで表示します。

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
    ifstream inputFile("example.txt");
    if (!inputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }

    string line;
    while (getline(inputFile, line)) {
        cout << line << endl;
    }

    inputFile.close();
    return 0;
}

このコードでは、「getline」関数を使ってファイルから1行ずつ読み込んでいます。ファイルの終わりに達すると、ループを終了します。

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

テキストファイルに書き込むには、「ofstream」を使用します。次の例では、「output.txt」というファイルにテキストを書き込んでいます。

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ofstream outputFile("output.txt");
    if (!outputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }

    outputFile << "これはテストファイルです。" << endl;
    outputFile << "C++でファイルに書き込みを行っています。" << endl;

    outputFile.close();
    return 0;
}

このコードでは、「<<」演算子を使ってファイルにテキストを出力しています。出力が完了した後は、ファイルを閉じます。

追加書き込み

既存のファイルにデータを追加したい場合は、「ofstream」のコンストラクタに「ios::app」フラグを渡します。

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ofstream outputFile("output.txt", ios::app);
    if (!outputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }

    outputFile << "追加のテキスト行です。" << endl;

    outputFile.close();
    return 0;
}

このコードでは、既存の「output.txt」ファイルに新しい行を追加しています。これにより、既存の内容は保持され、新しい内容が末尾に追加されます。

これらの操作をマスターすることで、C++を使ったテキストファイルの読み書きが効率よく行えるようになります。次のセクションでは、バイナリファイルの読み込みと書き込みについて詳しく説明します。

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

バイナリファイルは、テキストファイルとは異なり、データをそのままの形式で保存するため、画像や音声、プログラムの実行ファイルなどの保存に適しています。ここでは、C++を用いてバイナリファイルの読み込みと書き込み方法について解説します。

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

バイナリファイルを読み込むためには、「ifstream」を使用し、モードを「ios::binary」に設定します。以下の例では、バイナリファイル「data.bin」を読み込み、内容をバッファに格納しています。

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ifstream inputFile("data.bin", ios::binary);
    if (!inputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }

    inputFile.seekg(0, ios::end);
    size_t fileSize = inputFile.tellg();
    inputFile.seekg(0, ios::beg);

    char* buffer = new char[fileSize];
    inputFile.read(buffer, fileSize);

    // バッファの内容を処理
    for (size_t i = 0; i < fileSize; ++i) {
        cout << buffer[i];
    }

    delete[] buffer;
    inputFile.close();
    return 0;
}

このコードでは、ファイルサイズを取得してから、そのサイズ分のバッファを確保し、ファイルの内容を一括で読み込んでいます。

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

バイナリファイルにデータを書き込む際も、「ofstream」を使用し、モードを「ios::binary」に設定します。以下の例では、バッファに格納されたデータをバイナリファイル「output.bin」に書き込んでいます。

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ofstream outputFile("output.bin", ios::binary);
    if (!outputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }

    char data[] = "これはバイナリデータです";
    outputFile.write(data, sizeof(data));

    outputFile.close();
    return 0;
}

このコードでは、バッファ内のデータを「output.bin」ファイルに一括で書き込んでいます。

バイナリデータの操作例

実際のアプリケーションでは、バイナリデータの読み書きに加えて、データの解析や加工も行います。以下は、画像ファイルのヘッダー情報を読み取る例です。

#include <iostream>
#include <fstream>
using namespace std;

struct BitmapHeader {
    char fileType[2]; // ファイルタイプ
    int fileSize;     // ファイルサイズ
    int reserved;     // 予約領域
    int dataOffset;   // データオフセット
};

int main() {
    ifstream inputFile("image.bmp", ios::binary);
    if (!inputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }

    BitmapHeader header;
    inputFile.read(reinterpret_cast<char*>(&header), sizeof(header));

    cout << "ファイルタイプ: " << header.fileType[0] << header.fileType[1] << endl;
    cout << "ファイルサイズ: " << header.fileSize << endl;
    cout << "データオフセット: " << header.dataOffset << endl;

    inputFile.close();
    return 0;
}

この例では、画像ファイル「image.bmp」のヘッダー情報を構造体「BitmapHeader」に読み込み、その内容を表示しています。

これらの操作により、C++を用いたバイナリファイルの読み書きを効果的に行うことができます。次のセクションでは、ディレクトリの作成方法について説明します。

ディレクトリの作成方法

C++でディレクトリを作成する方法は、標準ライブラリを使用する方法と、プラットフォーム固有のAPIを使用する方法があります。このセクションでは、標準ライブラリを使用した方法を中心に説明します。

標準ライブラリを使用する

C++17以降では、標準ライブラリに「」ヘッダーが追加され、ディレクトリ操作が簡単に行えるようになりました。以下のコードは、新しいディレクトリを作成する方法を示しています。

#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

int main() {
    string dirName = "new_directory";
    if (!fs::create_directory(dirName)) {
        cerr << "ディレクトリの作成に失敗しました。" << endl;
        return 1;
    }

    cout << "ディレクトリを作成しました: " << dirName << endl;
    return 0;
}

このコードでは、「create_directory」関数を使用して「new_directory」という名前のディレクトリを作成しています。ディレクトリの作成が成功すると、メッセージが表示されます。

プラットフォーム固有のAPIを使用する

古いC++標準や特定のプラットフォーム向けには、プラットフォーム固有のAPIを使用する方法もあります。以下は、Windows APIとPOSIX APIを使った例です。

Windows APIを使用する

Windowsでは、「CreateDirectory」関数を使用してディレクトリを作成します。

#include <iostream>
#include <windows.h>
using namespace std;

int main() {
    string dirName = "new_directory";
    if (!CreateDirectory(dirName.c_str(), NULL)) {
        cerr << "ディレクトリの作成に失敗しました。" << endl;
        return 1;
    }

    cout << "ディレクトリを作成しました: " << dirName << endl;
    return 0;
}

POSIX APIを使用する

POSIX互換のシステム(LinuxやMacOS)では、「mkdir」関数を使用します。

#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
using namespace std;

int main() {
    string dirName = "new_directory";
    if (mkdir(dirName.c_str(), 0777) == -1) {
        cerr << "ディレクトリの作成に失敗しました。" << endl;
        return 1;
    }

    cout << "ディレクトリを作成しました: " << dirName << endl;
    return 0;
}

このコードでは、「mkdir」関数を使用してディレクトリを作成しています。パーミッションは「0777」で指定し、すべてのユーザーに対して読み取り、書き込み、実行の権限を与えています。

これらの方法を使うことで、C++でのディレクトリ作成が柔軟に行えます。次のセクションでは、ディレクトリの削除方法について説明します。

ディレクトリの削除方法

C++でディレクトリを削除する方法には、標準ライブラリを使用する方法とプラットフォーム固有のAPIを使用する方法があります。このセクションでは、標準ライブラリとプラットフォーム固有のAPIを使用したディレクトリの削除方法について詳しく説明します。

標準ライブラリを使用する

C++17以降では、標準ライブラリに「」ヘッダーが追加され、ディレクトリ操作が簡単に行えるようになりました。以下のコードは、ディレクトリを削除する方法を示しています。

#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

int main() {
    string dirName = "old_directory";
    if (!fs::remove(dirName)) {
        cerr << "ディレクトリの削除に失敗しました。" << endl;
        return 1;
    }

    cout << "ディレクトリを削除しました: " << dirName << endl;
    return 0;
}

このコードでは、「remove」関数を使用して「old_directory」という名前のディレクトリを削除しています。ディレクトリの削除が成功すると、メッセージが表示されます。

プラットフォーム固有のAPIを使用する

古いC++標準や特定のプラットフォーム向けには、プラットフォーム固有のAPIを使用する方法もあります。以下は、Windows APIとPOSIX APIを使った例です。

Windows APIを使用する

Windowsでは、「RemoveDirectory」関数を使用してディレクトリを削除します。

#include <iostream>
#include <windows.h>
using namespace std;

int main() {
    string dirName = "old_directory";
    if (!RemoveDirectory(dirName.c_str())) {
        cerr << "ディレクトリの削除に失敗しました。" << endl;
        return 1;
    }

    cout << "ディレクトリを削除しました: " << dirName << endl;
    return 0;
}

POSIX APIを使用する

POSIX互換のシステム(LinuxやMacOS)では、「rmdir」関数を使用します。

#include <iostream>
#include <unistd.h>
using namespace std;

int main() {
    string dirName = "old_directory";
    if (rmdir(dirName.c_str()) == -1) {
        cerr << "ディレクトリの削除に失敗しました。" << endl;
        return 1;
    }

    cout << "ディレクトリを削除しました: " << dirName << endl;
    return 0;
}

このコードでは、「rmdir」関数を使用してディレクトリを削除しています。削除が成功すると、メッセージが表示されます。

ディレクトリ削除の注意点

ディレクトリを削除する際は、そのディレクトリが空であることを確認する必要があります。中身があるディレクトリを削除しようとすると、エラーが発生します。そのため、ディレクトリを削除する前に、内部のファイルやサブディレクトリをすべて削除する必要があります。

#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

int main() {
    string dirName = "full_directory";
    try {
        fs::remove_all(dirName);
        cout << "ディレクトリとその内容をすべて削除しました: " << dirName << endl;
    } catch (const fs::filesystem_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、「remove_all」関数を使用して、ディレクトリとその中身をすべて削除しています。

これらの方法を使うことで、C++でディレクトリの削除が柔軟に行えます。次のセクションでは、ディレクトリの移動方法について説明します。

ディレクトリの移動方法

ディレクトリを移動することは、ファイルシステムを管理する上で重要な操作の一つです。C++では、標準ライブラリを使用してディレクトリを移動する方法と、プラットフォーム固有のAPIを使用する方法があります。このセクションでは、それぞれの方法について詳しく説明します。

標準ライブラリを使用する

C++17以降、標準ライブラリに「」ヘッダーが追加され、ディレクトリの移動が簡単に行えるようになりました。以下のコードは、ディレクトリを移動する方法を示しています。

#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

int main() {
    string sourceDir = "old_directory";
    string destinationDir = "new_location/old_directory";
    try {
        fs::rename(sourceDir, destinationDir);
        cout << "ディレクトリを移動しました: " << sourceDir << " -> " << destinationDir << endl;
    } catch (const fs::filesystem_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、「rename」関数を使用して「old_directory」というディレクトリを「new_location」フォルダ内に移動しています。

プラットフォーム固有のAPIを使用する

古いC++標準や特定のプラットフォーム向けには、プラットフォーム固有のAPIを使用する方法もあります。以下は、Windows APIとPOSIX APIを使った例です。

Windows APIを使用する

Windowsでは、「MoveFile」関数を使用してディレクトリを移動します。

#include <iostream>
#include <windows.h>
using namespace std;

int main() {
    string sourceDir = "old_directory";
    string destinationDir = "new_location\\old_directory";
    if (!MoveFile(sourceDir.c_str(), destinationDir.c_str())) {
        cerr << "ディレクトリの移動に失敗しました。" << endl;
        return 1;
    }

    cout << "ディレクトリを移動しました: " << sourceDir << " -> " << destinationDir << endl;
    return 0;
}

POSIX APIを使用する

POSIX互換のシステム(LinuxやMacOS)では、「rename」関数を使用します。

#include <iostream>
#include <unistd.h>
using namespace std;

int main() {
    string sourceDir = "old_directory";
    string destinationDir = "new_location/old_directory";
    if (rename(sourceDir.c_str(), destinationDir.c_str()) == -1) {
        cerr << "ディレクトリの移動に失敗しました。" << endl;
        return 1;
    }

    cout << "ディレクトリを移動しました: " << sourceDir << " -> " << destinationDir << endl;
    return 0;
}

このコードでは、「rename」関数を使用してディレクトリを移動しています。

ディレクトリ移動の注意点

ディレクトリを移動する際には、移動先に同名のディレクトリやファイルが存在しないことを確認する必要があります。また、移動元ディレクトリが使用中でないことも重要です。これらの確認を怠ると、移動操作が失敗する可能性があります。

#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

int main() {
    string sourceDir = "old_directory";
    string destinationDir = "new_location/old_directory";
    try {
        if (fs::exists(destinationDir)) {
            cerr << "移動先に同名のディレクトリが存在します。" << endl;
            return 1;
        }
        fs::rename(sourceDir, destinationDir);
        cout << "ディレクトリを移動しました: " << sourceDir << " -> " << destinationDir << endl;
    } catch (const fs::filesystem_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、移動先に同名のディレクトリが存在するかをチェックし、存在しない場合にのみディレクトリを移動しています。

これらの方法を使うことで、C++でディレクトリの移動が柔軟に行えます。次のセクションでは、ファイル入出力やディレクトリ操作時のエラーハンドリングについて詳しく説明します。

エラーハンドリング

ファイル入出力やディレクトリ操作を行う際には、さまざまなエラーが発生する可能性があります。エラーを適切に処理することで、プログラムの信頼性とユーザビリティを向上させることができます。このセクションでは、C++におけるエラーハンドリングの基本と具体例について説明します。

ファイル操作のエラーハンドリング

ファイルを開く際や読み書きの際にエラーが発生することがあります。これらのエラーを検出し、適切に対処する方法を紹介します。

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ifstream inputFile("nonexistent_file.txt");
    if (!inputFile) {
        cerr << "ファイルを開くことができませんでした。" << endl;
        return 1;
    }

    // ファイル操作のコード
    inputFile.close();
    return 0;
}

このコードでは、ファイルを開く際にエラーが発生した場合、「cerr」を使用してエラーメッセージを出力し、プログラムを終了します。

ディレクトリ操作のエラーハンドリング

ディレクトリの作成、削除、移動時にもエラーが発生することがあります。C++17以降では、「」ライブラリを使用してこれらのエラーをキャッチすることができます。

#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

int main() {
    string dirName = "example_directory";
    try {
        if (!fs::create_directory(dirName)) {
            cerr << "ディレクトリの作成に失敗しました。" << endl;
            return 1;
        }
        cout << "ディレクトリを作成しました: " << dirName << endl;
    } catch (const fs::filesystem_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、「create_directory」関数を使用してディレクトリを作成し、エラーが発生した場合には「filesystem_error」例外をキャッチしてエラーメッセージを表示します。

例外を用いたエラーハンドリング

C++では、例外を使用してエラーを処理することができます。これにより、エラー発生時にプログラムの実行を中断し、適切なエラーメッセージを表示することができます。

#include <iostream>
#include <fstream>
#include <stdexcept>
using namespace std;

void readFile(const string& fileName) {
    ifstream inputFile(fileName);
    if (!inputFile) {
        throw runtime_error("ファイルを開くことができません: " + fileName);
    }

    // ファイル操作のコード
    inputFile.close();
}

int main() {
    try {
        readFile("nonexistent_file.txt");
    } catch (const runtime_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、「readFile」関数内でファイルを開けなかった場合に「runtime_error」例外をスローし、「main」関数内でその例外をキャッチしてエラーメッセージを表示します。

ディレクトリ操作における例外処理

ディレクトリ操作でも、例外を使用してエラーを処理することができます。次の例では、ディレクトリの削除に失敗した場合に例外をスローします。

#include <iostream>
#include <filesystem>
#include <stdexcept>
using namespace std;
namespace fs = std::filesystem;

void removeDirectory(const string& dirName) {
    if (!fs::remove(dirName)) {
        throw runtime_error("ディレクトリの削除に失敗しました: " + dirName);
    }
}

int main() {
    try {
        removeDirectory("nonexistent_directory");
    } catch (const runtime_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、「removeDirectory」関数内でディレクトリの削除に失敗した場合に「runtime_error」例外をスローし、「main」関数内でその例外をキャッチしてエラーメッセージを表示します。

これらのエラーハンドリング手法を使用することで、ファイル入出力やディレクトリ操作の信頼性を向上させることができます。次のセクションでは、実践例としてファイルコピー機能の実装方法について説明します。

実践例: ファイルコピー機能の実装

ここでは、C++を使用してファイルをコピーする機能を実装する方法を具体的に紹介します。ファイルコピーは、ファイルのバックアップや転送など、さまざまな場面で必要となる基本的な操作です。

標準ライブラリを使用したファイルコピー

C++17以降では、標準ライブラリの「」ヘッダーを使用して簡単にファイルをコピーすることができます。

#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

int main() {
    string sourceFile = "source.txt";
    string destinationFile = "destination.txt";
    try {
        fs::copy(sourceFile, destinationFile, fs::copy_options::overwrite_existing);
        cout << "ファイルをコピーしました: " << sourceFile << " -> " << destinationFile << endl;
    } catch (const fs::filesystem_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、「copy」関数を使用して「source.txt」ファイルを「destination.txt」ファイルにコピーしています。「overwrite_existing」オプションを指定することで、既存のファイルがあっても上書きします。

低レベルAPIを使用したファイルコピー

低レベルAPIを使用して、ファイルの内容を手動で読み取り、書き込む方法もあります。以下のコードは、ストリームを使用してファイルをコピーする方法を示しています。

#include <iostream>
#include <fstream>
using namespace std;

void copyFile(const string& sourceFile, const string& destinationFile) {
    ifstream src(sourceFile, ios::binary);
    if (!src) {
        throw runtime_error("ソースファイルを開くことができません: " + sourceFile);
    }

    ofstream dest(destinationFile, ios::binary);
    if (!dest) {
        throw runtime_error("宛先ファイルを開くことができません: " + destinationFile);
    }

    dest << src.rdbuf();

    src.close();
    dest.close();
}

int main() {
    try {
        copyFile("source.txt", "destination.txt");
        cout << "ファイルをコピーしました: source.txt -> destination.txt" << endl;
    } catch (const runtime_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、「ifstream」と「ofstream」を使用して、ソースファイルから宛先ファイルにバイナリデータを読み込み、書き込んでいます。エラーが発生した場合には、「runtime_error」例外をスローし、適切にエラーメッセージを表示します。

ファイルコピーの注意点

ファイルコピーを行う際には、以下の点に注意する必要があります。

  • コピー先に同名のファイルが存在する場合、上書きするかどうかを確認する。
  • コピーするファイルが非常に大きい場合、メモリ使用量やコピー時間に注意する。
  • コピー中にエラーが発生した場合に適切に対処する。

これらの点を考慮することで、安全かつ効率的にファイルコピーを行うことができます。

#include <iostream>
#include <filesystem>
#include <stdexcept>
using namespace std;
namespace fs = std::filesystem;

void safeCopyFile(const string& sourceFile, const string& destinationFile) {
    if (!fs::exists(sourceFile)) {
        throw runtime_error("ソースファイルが存在しません: " + sourceFile);
    }

    if (fs::exists(destinationFile)) {
        cout << "警告: 宛先ファイルが既に存在します。上書きしますか? (y/n): ";
        char response;
        cin >> response;
        if (response != 'y' && response != 'Y') {
            throw runtime_error("ファイルコピーをキャンセルしました。");
        }
    }

    fs::copy(sourceFile, destinationFile, fs::copy_options::overwrite_existing);
}

int main() {
    try {
        safeCopyFile("source.txt", "destination.txt");
        cout << "ファイルをコピーしました: source.txt -> destination.txt" << endl;
    } catch (const runtime_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、コピー前にユーザーに確認を求める機能を追加しています。ユーザーが「y」または「Y」を入力した場合にのみ、上書きコピーを実行します。

これらの例を通じて、C++でのファイルコピー機能の実装方法を理解できるでしょう。次のセクションでは、応用例としてファイル検索ツールの作成方法について説明します。

応用: ファイル検索ツールの作成

ここでは、C++を使用して指定したディレクトリ内のファイルを検索するツールを作成する方法を紹介します。このツールは、ディレクトリを再帰的に探索し、特定の条件に一致するファイルを見つけ出します。

標準ライブラリを使用したファイル検索

C++17以降では、標準ライブラリの「」ヘッダーを使用して、ディレクトリを再帰的に検索することができます。以下のコードは、指定したディレクトリ内のすべてのファイルを一覧表示する方法を示しています。

#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

void searchFiles(const fs::path& dir) {
    for (const auto& entry : fs::recursive_directory_iterator(dir)) {
        if (fs::is_regular_file(entry)) {
            cout << "ファイル: " << entry.path() << endl;
        }
    }
}

int main() {
    string directory = "search_directory";
    try {
        if (!fs::exists(directory)) {
            throw runtime_error("指定されたディレクトリが存在しません: " + directory);
        }
        searchFiles(directory);
    } catch (const fs::filesystem_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、「recursive_directory_iterator」を使用して指定したディレクトリを再帰的に探索し、見つかったファイルのパスを表示しています。

特定の条件に一致するファイルの検索

次に、特定の拡張子を持つファイルを検索する方法を示します。例えば、「.txt」ファイルを検索する場合のコードは以下の通りです。

#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;

void searchFiles(const fs::path& dir, const string& extension) {
    for (const auto& entry : fs::recursive_directory_iterator(dir)) {
        if (fs::is_regular_file(entry) && entry.path().extension() == extension) {
            cout << "ファイル: " << entry.path() << endl;
        }
    }
}

int main() {
    string directory = "search_directory";
    string extension = ".txt";
    try {
        if (!fs::exists(directory)) {
            throw runtime_error("指定されたディレクトリが存在しません: " + directory);
        }
        searchFiles(directory, extension);
    } catch (const fs::filesystem_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、ファイルの拡張子が「.txt」であるかをチェックし、一致するファイルのみを表示しています。

正規表現を使用したファイル名の検索

さらに進んで、ファイル名が特定のパターンに一致するファイルを検索する方法を示します。正規表現を使用して、ファイル名が特定のパターンに一致するかをチェックします。

#include <iostream>
#include <filesystem>
#include <regex>
using namespace std;
namespace fs = std::filesystem;

void searchFiles(const fs::path& dir, const regex& pattern) {
    for (const auto& entry : fs::recursive_directory_iterator(dir)) {
        if (fs::is_regular_file(entry) && regex_search(entry.path().filename().string(), pattern)) {
            cout << "ファイル: " << entry.path() << endl;
        }
    }
}

int main() {
    string directory = "search_directory";
    regex pattern(".*\\.txt$"); // .txtで終わるファイルを検索
    try {
        if (!fs::exists(directory)) {
            throw runtime_error("指定されたディレクトリが存在しません: " + directory);
        }
        searchFiles(directory, pattern);
    } catch (const fs::filesystem_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、正規表現「.*\.txt$」を使用して、ファイル名が「.txt」で終わるファイルを検索しています。

ファイル検索ツールの実用例

これらの技術を組み合わせることで、より高度なファイル検索ツールを作成できます。例えば、特定のディレクトリ内で指定したキーワードを含むテキストファイルを検索するツールを作成することができます。

#include <iostream>
#include <filesystem>
#include <fstream>
#include <regex>
using namespace std;
namespace fs = std::filesystem;

void searchFiles(const fs::path& dir, const regex& pattern, const string& keyword) {
    for (const auto& entry : fs::recursive_directory_iterator(dir)) {
        if (fs::is_regular_file(entry) && regex_search(entry.path().filename().string(), pattern)) {
            ifstream file(entry.path());
            string line;
            while (getline(file, line)) {
                if (line.find(keyword) != string::npos) {
                    cout << "一致するファイル: " << entry.path() << " キーワード: " << keyword << endl;
                    break;
                }
            }
        }
    }
}

int main() {
    string directory = "search_directory";
    regex pattern(".*\\.txt$"); // .txtで終わるファイルを検索
    string keyword = "特定のキーワード"; // 検索するキーワード
    try {
        if (!fs::exists(directory)) {
            throw runtime_error("指定されたディレクトリが存在しません: " + directory);
        }
        searchFiles(directory, pattern, keyword);
    } catch (const fs::filesystem_error& e) {
        cerr << "エラー: " << e.what() << endl;
        return 1;
    }

    return 0;
}

このコードでは、指定したディレクトリ内で「.txt」ファイルを検索し、その中に特定のキーワードが含まれているかをチェックしています。一致するファイルが見つかると、そのファイルのパスを表示します。

これらの例を通じて、C++でのファイル検索ツールの作成方法を理解できるでしょう。次のセクションでは、本記事の内容をまとめます。

まとめ

本記事では、C++を用いたファイル入出力とディレクトリ操作について、基本から応用まで詳しく解説しました。以下に主要なポイントをまとめます。

  1. ファイル入出力の基本: 「ifstream」や「ofstream」を使用してファイルを読み書きする方法を学びました。
  2. テキストファイルの操作: テキストファイルの読み込みと書き込み方法について、具体的なコード例を通じて説明しました。
  3. バイナリファイルの操作: バイナリファイルの読み込みと書き込みの手法を紹介し、実際のデータ操作例を示しました。
  4. ディレクトリの作成・削除・移動: C++17以降の「」ライブラリを用いて、ディレクトリの作成、削除、移動を行う方法を学びました。
  5. エラーハンドリング: ファイルやディレクトリ操作時のエラーハンドリングの重要性と具体的な実装方法を解説しました。
  6. 実践例: ファイルコピー機能の実装方法を示し、標準ライブラリや低レベルAPIを使用した具体的な例を紹介しました。
  7. 応用例: ディレクトリ内のファイルを検索するツールを作成する方法を紹介し、正規表現や再帰的なディレクトリ探索の実例を示しました。

これらの知識を活用することで、C++におけるファイル入出力やディレクトリ操作のスキルを向上させ、実用的なプログラムを作成できるようになります。この記事が、皆さんのプログラミングの参考になれば幸いです。

コメント

コメントする

目次
  1. C++でのファイル入出力の基本
    1. ファイルを開く
    2. ファイルの読み込み
    3. ファイルへの書き込み
  2. テキストファイルの読み込みと書き込み
    1. テキストファイルの読み込み
    2. テキストファイルへの書き込み
    3. 追加書き込み
  3. バイナリファイルの読み込みと書き込み
    1. バイナリファイルの読み込み
    2. バイナリファイルへの書き込み
    3. バイナリデータの操作例
  4. ディレクトリの作成方法
    1. 標準ライブラリを使用する
    2. プラットフォーム固有のAPIを使用する
  5. ディレクトリの削除方法
    1. 標準ライブラリを使用する
    2. プラットフォーム固有のAPIを使用する
    3. ディレクトリ削除の注意点
  6. ディレクトリの移動方法
    1. 標準ライブラリを使用する
    2. プラットフォーム固有のAPIを使用する
    3. ディレクトリ移動の注意点
  7. エラーハンドリング
    1. ファイル操作のエラーハンドリング
    2. ディレクトリ操作のエラーハンドリング
    3. 例外を用いたエラーハンドリング
    4. ディレクトリ操作における例外処理
  8. 実践例: ファイルコピー機能の実装
    1. 標準ライブラリを使用したファイルコピー
    2. 低レベルAPIを使用したファイルコピー
    3. ファイルコピーの注意点
  9. 応用: ファイル検索ツールの作成
    1. 標準ライブラリを使用したファイル検索
    2. 特定の条件に一致するファイルの検索
    3. 正規表現を使用したファイル名の検索
    4. ファイル検索ツールの実用例
  10. まとめ