閉じる

【C++】ファイルを削除する方法|remove関数とfilesystemの使い方

C++でプログラミングを行っている際、ログファイルの整理や一時ファイルの消去など、ファイルをプログラム上から削除したい場面は多々あります。

かつてはC言語由来の関数を使用するのが一般的でしたが、現在のモダンなC++ではより安全で強力なライブラリが用意されています。

本記事では、古くから使われているremove関数から、C++17以降の標準であるstd::filesystemを用いた高度な削除方法までを詳しく解説します。

OSごとの挙動の違いやエラーハンドリングのポイントも踏まえ、実践的なコードとともに見ていきましょう。

C++におけるファイル削除の全体像

C++でファイルを削除する方法は、大きく分けて2つの系統が存在します。

1つはC言語の標準ライブラリを継承した方法、もう1つはC++17で導入されたファイルシステムライブラリを使用する方法です。

どちらの方法を選ぶべきか

結論から述べると、C++17以降が利用可能な環境であれば、std::filesystemを使用することを強く推奨します

理由は、ファイルパスの扱いがオブジェクト指向的で直感的であり、例外処理やエラーコードの取得が容易だからです。

一方で、非常に古い組み込み環境や、極限までバイナリサイズを削る必要がある特殊なケースでは、シンプルなstd::removeが使われることもあります。

しかし、一般的なアプリケーション開発においては、安全性の観点からモダンな機能を使うのが現在のスタンダードです。

std::remove関数によるシンプルな削除

まずは、最も基本的な方法であるstd::remove関数について解説します。

この関数は<cstdio>ヘッダに定義されており、非常にシンプルなインターフェースを持っています。

std::removeの使い方

std::removeは、削除したいファイルのパスを文字列(char型のポインタ)として受け取ります。

戻り値は整数値で、成功した場合は0を返し、失敗した場合は0以外を返します

サンプルコード:std::removeの使用例

C++
#include <iostream>
#include <cstdio> // std::removeを使用するために必要

int main() {
    const char* filename = "test_file.txt";

    // ファイルの削除を実行
    if (std::remove(filename) == 0) {
        // 戻り値が0なら成功
        std::cout << "ファイル '" << filename << "' を正常に削除しました。" << std::endl;
    } else {
        // 0以外なら失敗。perrorで詳細なエラー内容を表示できる
        std::perror("ファイル削除エラー");
    }

    return 0;
}
実行結果
ファイル 'test_file.txt' を正常に削除しました。

※ファイルが存在しない場合は「ファイル削除エラー: No such file or directory」などのエラーが表示されます。

std::removeの注意点

この関数は非常にシンプルですが、いくつかの欠点があります。

まず、ディレクトリを削除することができません(OSによっては可能な場合もありますが、標準ではファイル用です)。

また、パスの中に日本語などのマルチバイト文字が含まれている場合、エンコードの問題で正しく認識されないことがあります。

さらに、詳細なエラー原因を知るためにはerrnoを確認する必要があり、現代的なC++のコーディングスタイルとはやや相性が悪いという側面があります。

std::filesystem::removeによる現代的な削除

C++17から導入されたstd::filesystemライブラリは、ファイル操作を劇的に進化させました。

このライブラリを使用すると、ファイルかディレクトリかを問わず、一貫した方法で削除が可能になります。

std::filesystem::removeの基本

この関数は、引数にstd::filesystem::pathを受け取ります。

文字列を直接渡しても、暗黙的にパスオブジェクトに変換されます。

サンプルコード:例外処理を用いた削除

C++
#include <iostream>
#include <filesystem> // C++17以降が必要

namespace fs = std::filesystem;

int main() {
    fs::path targetPath = "modern_test.txt";

    try {
        // ファイルまたは空のディレクトリを削除
        // 削除された場合はtrue、存在しなかった場合はfalseを返す
        bool result = fs::remove(targetPath);

        if (result) {
            std::cout << "削除に成功しました。" << std::endl;
        } else {
            std::cout << "ファイルが見つかりませんでした。" << std::endl;
        }
    } catch (const fs::filesystem_error& e) {
        // アクセス権限不足などのエラーが発生した場合
        std::cerr << "エラーが発生しました: " << e.what() << std::endl;
    }

    return 0;
}
実行結果
削除に成功しました。

エラーコードを使用する方法

例外をスローさせたくない場合は、第2引数にstd::error_codeを渡します。

これにより、プログラムの実行を止めずにエラーの有無を確認できます。

C++
#include <iostream>
#include <filesystem>
#include <system_error>

namespace fs = std::filesystem;

int main() {
    std::error_code ec;
    fs::path targetPath = "secure_delete.log";

    if (fs::remove(targetPath, ec)) {
        std::cout << "削除成功" << std::endl;
    } else {
        if (ec) {
            // エラーが発生している場合
            std::cerr << "削除失敗原因: " << ec.message() << std::endl;
        } else {
            // エラーはないが削除もされていない(ファイルが存在しない)
            std::cout << "ファイルが存在しませんでした。" << std::endl;
        }
    }

    return 0;
}

フォルダの中身ごと削除する remove_all

std::filesystem::removeには一つ制限があります。

それは、中身が空ではないディレクトリは削除できないという点です。

もしフォルダの中にあるファイルやサブフォルダも含めて一括で削除したい場合は、std::filesystem::remove_allを使用します。

remove_allの威力とリスク

remove_allは非常に強力です。

指定したパスがディレクトリであれば、その中の全ファイルを再帰的に削除します。

サンプルコード:ディレクトリの再帰的削除

C++
#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main() {
    fs::path dirPath = "temp_directory";

    // 削除されたアイテムの数(ファイル+ディレクトリ)を返す
    std::uintmax_t deletedCount = fs::remove_all(dirPath);

    std::cout << "合計 " << deletedCount << " 個のアイテムを削除しました。" << std::endl;

    return 0;
}
実行結果
合計 5 個のアイテムを削除しました。

この関数を使用する際は、削除対象のパスが意図したものであるかを十分に確認してください。

ルートディレクトリに近い場所を誤って指定すると、システムに甚大な被害を与える可能性があります。

実践的なエラーハンドリングと注意点

ファイルを削除する処理は、外部要因によって失敗する可能性が常にあります。

堅牢なプログラムを作成するためには、以下のポイントを考慮する必要があります。

ファイルが使用中である場合

Windows環境などでよく発生するのが、「別のプロセスがファイルを開いているため削除できない」というケースです。

この場合、OS側がファイルロックをかけているため、C++側から強制的に削除することは困難です。

std::filesystem::removeはこのような時に例外を投げるか、エラーコードを返します。

リトライ処理を入れるか、ユーザーにファイルを閉じるよう促すメッセージを表示するのが一般的です。

アクセス権限の確認

管理者権限が必要なディレクトリ内のファイルを削除しようとすると、Permission Deniedエラーが発生します。

プログラムを実行するユーザーに適切な権限が付与されているか、設計段階で確認が必要です。

削除前の存在確認

「ファイルが存在しない場合にエラーにしたくない」という場合は、削除前にfs::exists()を使って確認する方法があります。

ただし、確認してから削除するまでの僅かな時間に他プロセスがファイルを消す可能性(TOCTOU問題)があるため、マルチスレッド環境などでは注意が必要です。

ファイル削除方法の比較表

各手法の違いを整理しました。

機能std::removefs::removefs::remove_all
導入時期C言語(初期)C++17C++17
ヘッダ<cstdio><filesystem><filesystem>
対象ファイルファイル/空のディレクトリファイル/全ディレクトリ
エラー通知戻り値(int)例外 or エラーコード例外 or エラーコード
パスの扱いC文字列のみpath オブジェクトpath オブジェクト
再帰削除不可不可可能

まとめ

C++でファイルを削除する方法は、単純な削除であればstd::removeでも可能ですが、現代の標準的な開発では std::filesystem::remove を使用するのが最適です。

特にstd::filesystemは、パスの操作、例外処理、再帰的なディレクトリ削除など、ファイル操作に必要な機能が網羅されています。

エラーハンドリングを適切に行うことで、ファイルロックや権限不足といった実行時のトラブルにも柔軟に対応できるようになります。

まずは、自分の開発環境がC++17以上に対応しているかを確認し、積極的に新しいライブラリを活用していきましょう。

安全でクリーンなコードを書くことが、バグの少ないシステム開発への第一歩となります。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!