C++プログラミング必携!is_regular_fileでファイル操作をマスター

2025-05-27

C++のstd::filesystem::is_regular_fileは、指定されたパスが通常のファイルであるかどうかを判定するための関数です。

もう少し詳しく説明すると、以下のようになります。

std::filesystemとは?

まず、std::filesystemはC++17で導入された標準ライブラリで、ファイルシステム(ファイルやディレクトリの操作)を扱うための機能を提供します。これを使うことで、OSに依存しない形でファイルの存在確認、作成、削除、パスの操作などができるようになります。

is_regular_fileの役割

is_regular_fileは、特定のパスが指し示すものが「通常のファイル」であるかを判定します。ここでいう「通常のファイル」とは、一般的にデータが保存されるテキストファイル、バイナリファイル、画像ファイルなどを指します。ディレクトリ(フォルダ)やシンボリックリンク、デバイスファイルなど、ファイルシステム上の他の種類のオブジェクトとは区別されます。

関数の使い方

is_regular_fileには主に2つのオーバーロードがあります。

  • file_statusオブジェクトを渡す場合
    std::filesystem::status関数などで事前にファイルのステータスを取得しておき、それをis_regular_fileに渡すこともできます。

    #include <iostream>
    #include <filesystem>
    
    int main() {
        std::filesystem::path p = "example.txt";
        std::filesystem::file_status s = std::filesystem::status(p);
    
        if (std::filesystem::is_regular_file(s)) {
            std::cout << p << " は通常のファイルです。" << std::endl;
        } else {
            std::cout << p << " は通常のファイルではありません。" << std::endl;
        }
        return 0;
    }
    

    この方法は、複数のファイル属性を一度に調べたい場合などに効率的です。

  • パスを直接渡す場合

    #include <iostream>
    #include <filesystem>
    
    int main() {
        std::filesystem::path p = "example.txt"; // 存在しないファイル
        // std::filesystem::path p = "my_directory"; // ディレクトリの場合
    
        if (std::filesystem::is_regular_file(p)) {
            std::cout << p << " は通常のファイルです。" << std::endl;
        } else {
            std::cout << p << " は通常のファイルではありません。" << std::endl;
        }
    
        // 実際にファイルを作成して試す
        std::ofstream ofs("test_file.txt");
        ofs << "Hello";
        ofs.close();
    
        std::filesystem::path existing_file = "test_file.txt";
        if (std::filesystem::is_regular_file(existing_file)) {
            std::cout << existing_file << " は通常のファイルです。" << std::endl;
        } else {
            std::cout << existing_file << " は通常のファイルではありません。" << std::endl;
        }
        std::filesystem::remove(existing_file); // テストファイルを削除
    
        return 0;
    }
    

    この場合、is_regular_fileは内部的にファイルのステータス(種類)を取得し、それが通常のファイルであるかを判定します。

戻り値

is_regular_filebool値を返します。

  • false: パスが通常のファイルではない(ディレクトリ、シンボリックリンク、存在しないパス、未知のタイプなど)。
  • true: パスが指し示すものが通常のファイルである。

std::filesystemの多くの関数と同様に、is_regular_fileにもエラーを報告する方法がいくつかあります。

  • std::error_codeを引数に取るオーバーロード
    例外を避けたい場合は、std::error_codeオブジェクトを引数に取るオーバーロードを使用できます。この場合、エラーが発生しても例外はスローされず、error_codeオブジェクトにエラー情報が格納されます。
  • 例外をスローするオーバーロード
    デフォルトでは、ファイルシステムエラーが発生した場合(例: 存在しないパス、アクセス権がないなど)にstd::filesystem::filesystem_error例外をスローします。


std::filesystem::filesystem_error 例外

これは最も一般的なエラーで、ファイルシステム操作中に何らかの問題が発生した場合にスローされます。is_regular_fileが例外をスローする場合、通常は以下の原因が考えられます。

  • 無効なパス指定
    パスに無効な文字が含まれていたり、パスの形式がオペレーティングシステムに合っていない場合に発生します。
    • トラブルシューティング
      パスの文字列が正しいか、特にOS固有のパス区切り文字(Windowsでは\、Linux/macOSでは/)が正しく使われているかを確認します。生文字列リテラル(R"(...))を使用すると、エスケープシーケンスの問題を避けられます。
      std::filesystem::path p_win = R"(C:\Users\YourUser\Documents\my_file.txt)"; // Windows
      std::filesystem::path p_linux = "/home/youruser/documents/my_file.txt"; // Linux/macOS
      
  • アクセス権がない
    指定されたパスへの読み取り権限がない場合に発生します。
    • トラブルシューティング
      プログラムを実行しているユーザーに、そのファイルまたはディレクトリへの適切なアクセス権があることを確認してください。Linux/macOSではls -l、Windowsではファイルのプロパティを確認できます。
  • 指定されたパスが存在しない
    ファイルやディレクトリが存在しない場合に発生します。
    • トラブルシューティング
      std::filesystem::exists()を使って、is_regular_fileを呼び出す前にパスが存在するかどうかを確認します。
      #include <iostream>
      #include <filesystem>
      
      int main() {
          std::filesystem::path p = "non_existent_file.txt";
          try {
              if (std::filesystem::exists(p)) {
                  if (std::filesystem::is_regular_file(p)) {
                      std::cout << p << " は通常のファイルです。" << std::endl;
                  } else {
                      std::cout << p << " は通常のファイルではありません (ディレクトリ、シンボリックリンクなど)。" << std::endl;
                  }
              } else {
                  std::cout << p << " は存在しません。" << std::endl;
              }
          } catch (const std::filesystem::filesystem_error& e) {
              std::cerr << "ファイルシステムエラー: " << e.what() << " (コード: " << e.code() << ")" << std::endl;
          }
          return 0;
      }
      

例外をスローしないオーバーロードの使用(std::error_code)

std::filesystem::filesystem_error例外の発生を防ぎたい場合は、std::error_codeを引数に取るオーバーロードを使用することが推奨されます。これにより、エラーが発生してもプログラムがクラッシュせず、エラー情報をコードで処理できます。

#include <iostream>
#include <filesystem>
#include <system_error> // std::error_code のために必要

int main() {
    std::filesystem::path p = "non_existent_file.txt";
    std::error_code ec; // エラー情報を格納するオブジェクト

    if (std::filesystem::is_regular_file(p, ec)) {
        std::cout << p << " は通常のファイルです。" << std::endl;
    } else {
        if (ec) { // ec にエラー情報が格納されている場合
            std::cerr << "ファイルシステムエラー: " << ec.message() << " (コード: " << ec.value() << ")" << std::endl;
        } else { // エラーは発生しなかったが、通常のファイルではなかった場合
            std::cout << p << " は通常のファイルではありません (ディレクトリ、シンボリックリンクなど、または存在しません)。" << std::endl;
            // exists() を追加でチェックするとより明確になります
            if (!std::filesystem::exists(p)) {
                std::cout << p << " は存在しません。" << std::endl;
            }
        }
    }
    return 0;
}
  • トラブルシューティング
    ectrue(エラーが発生した場合)かどうかをチェックし、そのmessage()value()を使って具体的なエラー内容を把握します。

パスがディレクトリを指している

is_regular_fileは「通常のファイル」を判定するため、ディレクトリを指しているパスに対してはfalseを返します。これはエラーではありませんが、意図しない結果である可能性があります。

  • トラブルシューティング
    パスがファイルかディレクトリかを両方チェックしたい場合は、std::filesystem::is_directory()も併用します。
    #include <iostream>
    #include <filesystem>
    
    int main() {
        std::filesystem::path p_file = "my_file.txt";
        std::filesystem::path p_dir = "my_directory";
    
        // ファイルを作成
        std::ofstream ofs(p_file);
        ofs.close();
        // ディレクトリを作成
        std::filesystem::create_directory(p_dir);
    
        if (std::filesystem::is_regular_file(p_file)) {
            std::cout << p_file << " は通常のファイルです。" << std::endl; // true
        }
        if (std::filesystem::is_directory(p_file)) {
            std::cout << p_file << " はディレクトリです。" << std::endl; // false
        }
    
        if (std::filesystem::is_regular_file(p_dir)) {
            std::cout << p_dir << " は通常のファイルです。" << std::endl; // false
        }
        if (std::filesystem::is_directory(p_dir)) {
            std::cout << p_dir << " はディレクトリです。" << std::endl; // true
        }
    
        // クリーンアップ
        std::filesystem::remove(p_file);
        std::filesystem::remove(p_dir);
    
        return 0;
    }
    

シンボリックリンクの扱い

is_regular_fileは、シンボリックリンク自体が通常のファイルであるかどうかではなく、シンボリックリンクが指す実体が通常のファイルであるかどうかをチェックします。

  • トラブルシューティング
    シンボリックリンク自体がシンボリックリンクであるかを判定したい場合は、std::filesystem::is_symlink()を使用します。
    #include <iostream>
    #include <filesystem>
    
    int main() {
        std::filesystem::path target_file = "original_file.txt";
        std::filesystem::path symlink_to_file = "symlink_to_original.txt";
    
        // 元のファイルを作成
        std::ofstream ofs(target_file);
        ofs << "Hello";
        ofs.close();
    
        // シンボリックリンクを作成 (Linux/macOS)
        // Windowsでは通常、管理者権限が必要または特殊な設定が必要です。
        // std::filesystem::create_symlink(target_file, symlink_to_file);
    
        // 例として、シンボリックリンクが作成されたと仮定した場合
        if (std::filesystem::is_regular_file(target_file)) {
            std::cout << target_file << " は通常のファイルです。" << std::endl; // true
        }
        // if (std::filesystem::is_regular_file(symlink_to_file)) {
        //     std::cout << symlink_to_file << " は通常のファイルです (指す先が)。" << std::endl; // true (指す先がファイルの場合)
        // }
        // if (std::filesystem::is_symlink(symlink_to_file)) {
        //     std::cout << symlink_to_file << " はシンボリックリンクです。" << std::endl; // true
        // }
    
        // クリーンアップ
        std::filesystem::remove(target_file);
        // std::filesystem::remove(symlink_to_file);
    
        return 0;
    }
    

コンパイルエラー(std::filesystemが見つからない)

C++17以前の標準を使用している場合、std::filesystemは利用できません。

  • トラブルシューティング
    • コンパイラがC++17以降の標準をサポートしていることを確認し、コンパイルオプションでC++17以降を指定します(例: GCC/Clangなら-std=c++17)。
    • 古いコンパイラを使用している場合は、Boost Filesystemライブラリを使用することも検討できます(Boostはstd::filesystemの基になったライブラリです)。

is_regular_fileを呼び出した直後に、別のプロセスやスレッドによってファイルが削除されたり、タイプが変更されたりする可能性があります。これにより、is_regular_filetrueを返したにもかかわらず、その後のファイル操作が失敗するような競合状態が発生することがあります。

  • トラブルシューティング
    • 特にマルチスレッド環境や、ファイルが頻繁に操作される可能性のある環境では、このような競合状態を完全に避けることは困難です。
    • 可能であれば、ファイル操作全体をアトミックに行うか、ファイルロックなどのメカニズムを検討します。
    • 多くの場合、is_regular_fileでチェックするのではなく、実際にファイルを開いてみてエラーを処理するというアプローチがより堅牢です。ファイルを開く試み自体が、その時点でファイルが利用可能で通常のファイルであるかどうかの最も確実なテストになるためです。


例1: 基本的な使用法 - ファイルが存在する場合と存在しない場合

この例では、実際にファイルを作成してis_regular_fileの動作を確認します。

#include <iostream>
#include <filesystem> // std::filesystem を使用するために必要
#include <fstream>    // ファイル操作(ファイルの作成、削除)のために必要

namespace fs = std::filesystem; // 短縮形としてエイリアスを設定

int main() {
    // 1. 存在しないファイルパス
    fs::path non_existent_file = "non_existent_file.txt";
    std::cout << "パス: " << non_existent_file << std::endl;
    if (fs::is_regular_file(non_existent_file)) {
        std::cout << "  -> 通常のファイルです。" << std::endl;
    } else {
        std::cout << "  -> 通常のファイルではありません (存在しないか、他のタイプ)。" << std::endl;
    }
    std::cout << "-----------------------------------" << std::endl;

    // 2. 通常のファイルを作成し、確認
    fs::path regular_file = "my_document.txt";
    // ファイルを作成し、内容を書き込む
    std::ofstream ofs(regular_file);
    if (ofs.is_open()) {
        ofs << "これはテストファイルです。" << std::endl;
        ofs.close();
        std::cout << "ファイル '" << regular_file << "' を作成しました。" << std::endl;
    } else {
        std::cerr << "エラー: ファイル '" << regular_file << "' を作成できませんでした。" << std::endl;
        return 1;
    }

    std::cout << "パス: " << regular_file << std::endl;
    if (fs::is_regular_file(regular_file)) {
        std::cout << "  -> 通常のファイルです。" << std::endl;
    } else {
        std::cout << "  -> 通常のファイルではありません。" << std::endl;
    }
    std::cout << "-----------------------------------" << std::endl;

    // 3. ディレクトリを作成し、確認
    fs::path directory_path = "my_directory";
    if (fs::create_directory(directory_path)) {
        std::cout << "ディレクトリ '" << directory_path << "' を作成しました。" << std::endl;
    } else {
        std::cerr << "エラー: ディレクトリ '" << directory_path << "' を作成できませんでした。" << std::endl;
        // エラーの場合は処理を中断せずに進める
    }

    std::cout << "パス: " << directory_path << std::endl;
    if (fs::is_regular_file(directory_path)) {
        std::cout << "  -> 通常のファイルです。" << std::endl;
    } else {
        std::cout << "  -> 通常のファイルではありません (ディレクトリです)。" << std::endl;
    }
    std::cout << "-----------------------------------" << std::endl;

    // 後処理: 作成したファイルとディレクトリを削除
    fs::remove(regular_file);
    fs::remove(directory_path);
    std::cout << "作成したファイルとディレクトリを削除しました。" << std::endl;

    return 0;
}

解説

  • is_regular_file(directory_path): 作成したディレクトリに対してはfalseを返します。
  • is_regular_file(regular_file): 作成した通常のファイルに対してはtrueを返します。
  • is_regular_file(non_existent_file): 存在しないパスに対してはfalseを返します。
  • fs::create_directory(directory_path): my_directoryという名前のディレクトリを作成します。
  • std::ofstream(regular_file): my_document.txtという名前のファイルを作成し、書き込み用に開きます。

例2: エラーコードを使用した例外安全なチェック

std::error_codeを引数に取るオーバーロードを使用することで、ファイルシステムエラーが発生しても例外がスローされず、エラー情報を処理できます。

#include <iostream>
#include <filesystem>
#include <fstream>
#include <system_error> // std::error_code のために必要

namespace fs = std::filesystem;

int main() {
    fs::path test_path = "invalid_path\0with_null.txt"; // null文字を含む無効なパス
    std::error_code ec; // エラーコードを格納するオブジェクト

    std::cout << "パス: " << test_path << std::endl;

    // is_regular_file を error_code オーバーロードで呼び出す
    if (fs::is_regular_file(test_path, ec)) {
        std::cout << "  -> 通常のファイルです。" << std::endl;
    } else {
        if (ec) { // エラーが発生した場合
            std::cerr << "  -> エラーが発生しました: " << ec.message() << " (コード: " << ec.value() << ")" << std::endl;
        } else { // エラーは発生しなかったが、通常のファイルではなかった場合
            std::cout << "  -> 通常のファイルではありません (存在しないか、他のタイプ)。" << std::endl;
            // 存在チェックを加えて、より詳細な情報を提供することも可能
            if (!fs::exists(test_path, ec)) { // exists も error_code オーバーロードを使用
                if (ec) {
                     std::cerr << "  -> exists()でもエラー: " << ec.message() << std::endl;
                } else {
                    std::cout << "  -> パスは存在しません。" << std::endl;
                }
            }
        }
    }

    std::cout << "-----------------------------------" << std::endl;

    // 存在するファイルに対してエラーなしで呼び出す例
    fs::path existing_file = "temp_file_for_error_check.txt";
    std::ofstream ofs(existing_file);
    ofs << "temporary content";
    ofs.close();

    std::error_code ec_no_error;
    if (fs::is_regular_file(existing_file, ec_no_error)) {
        std::cout << "パス: " << existing_file << std::endl;
        std::cout << "  -> 通常のファイルです。" << std::endl;
    } else {
        if (ec_no_error) {
            std::cerr << "  -> エラーが発生しました: " << ec_no_error.message() << std::endl;
        } else {
            std::cout << "  -> 通常のファイルではありません。" << std::endl;
        }
    }
    fs::remove(existing_file);

    return 0;
}

解説

  • ec.value(): エラーの数値コードを返します。
  • ec.message(): エラーの人間が読める形式のメッセージを返します。
  • if (ec): エラーコードオブジェクトが非ゼロの場合(つまりエラーが発生した場合)に真となります。
  • fs::is_regular_file(test_path, ec): 関数が失敗した場合、ecにエラーコードが設定されます。成功した場合はecはクリアされます。
  • std::error_code ec;: std::error_code型のオブジェクトを宣言します。このオブジェクトがエラー情報を受け取ります。

この方法により、ファイルシステムのエラーを捕捉し、適切にログ出力したり、フォールバック処理を行ったりすることができます。

std::filesystem::directory_iteratoris_regular_fileを組み合わせて、指定されたディレクトリ内のすべての通常のファイルをリストアップする例です。

#include <iostream>
#include <filesystem>
#include <fstream> // ファイル作成用

namespace fs = std::filesystem;

int main() {
    fs::path target_directory = "my_test_dir";

    // テスト用のディレクトリとファイルを作成
    fs::create_directory(target_directory);
    std::ofstream(target_directory / "file1.txt") << "content";
    std::ofstream(target_directory / "file2.log") << "log content";
    fs::create_directory(target_directory / "subdirectory");
    std::ofstream(target_directory / "subdirectory" / "nested_file.doc") << "doc content"; // サブディレクトリ内のファイル

    std::cout << "ディレクトリ '" << target_directory << "' 内の通常のファイル:" << std::endl;

    try {
        // directory_iterator を使用してディレクトリ内のエントリを反復処理
        for (const auto& entry : fs::directory_iterator(target_directory)) {
            // is_regular_file を使用して、現在のエントリが通常のファイルかどうかをチェック
            // directory_entry クラスにも is_regular_file メンバー関数があります
            if (entry.is_regular_file()) {
                std::cout << "  - " << entry.path().filename() << std::endl;
            }
        }
    } catch (const fs::filesystem_error& e) {
        std::cerr << "ファイルシステムエラー: " << e.what() << std::endl;
    }

    // 後処理: 作成したディレクトリとファイルを削除
    fs::remove_all(target_directory); // ディレクトリとその中のすべてを再帰的に削除
    std::cout << "テストディレクトリ '" << target_directory << "' を削除しました。" << std::endl;

    return 0;
}
  • fs::remove_all(target_directory): target_directoryとその中にあるすべてのファイルとサブディレクトリを再帰的に削除します。テスト後のクリーンアップに便利です。
  • entry.path().filename(): エントリのフルパスからファイル名(ディレクトリ名)だけを取得します。
  • entry.is_regular_file(): directory_entryオブジェクト自体がis_regular_fileメンバー関数を持っています。これは内部的にstd::filesystem::is_regular_file(entry.status())を呼び出すのと同じです。
  • fs::directory_iterator(target_directory): 指定されたディレクトリ内の各エントリを順に処理するためのイテレータです。
  • std::ofstream(target_directory / "file1.txt"): std::filesystem::pathオブジェクトと文字列を/演算子で結合して、新しいパスを作成できます。これはパスの結合に便利です。
  • fs::create_directory(target_directory): テスト用のディレクトリを作成します。


C++17 以前では、std::filesystem は標準ライブラリには含まれていませんでした。この場合、主に以下の選択肢があります。

a. Boost.Filesystem ライブラリ

std::filesystem は、Boost.Filesystem ライブラリを基に標準化されました。そのため、Boost.Filesystem は std::filesystem と非常に似たインターフェースを提供しており、C++17 以前の環境で同様の機能を実現するための最も一般的な選択肢です。

  • 注意点
    Boostライブラリをインストールし、プロジェクトにリンクする必要があります。
  • 使用例
    #include <iostream>
    #include <boost/filesystem.hpp> // Boost.Filesystem を使用
    
    namespace bf = boost::filesystem; // 短縮形としてエイリアスを設定
    
    int main() {
        bf::path p = "my_file.txt"; // Boost.Filesystem のパスオブジェクト
    
        // ファイルを作成 (Boost.Filesystem とは直接関係ないが、例のために)
        std::ofstream ofs("my_file.txt");
        ofs.close();
    
        if (bf::is_regular_file(p)) {
            std::cout << p << " は通常のファイルです。(Boost)" << std::endl;
        } else {
            std::cout << p << " は通常のファイルではありません。(Boost)" << std::endl;
        }
    
        // 後処理
        bf::remove(p);
    
        return 0;
    }
    
  • 特徴
    • std::filesystem とほぼ同じ関数名とセマンティクスを持つ。
    • クロスプラットフォーム。
    • 活発に開発・メンテナンスされている。

b. POSIX (Unix-like systems) の stat / lstat 関数

Linux, macOS などの Unix 系OSでは、sys/stat.h ヘッダに含まれる stat または lstat 関数を使用してファイルやディレクトリの情報を取得できます。

  • 注意点
    • Windows では直接使用できません。代わりに GetFileAttributesGetFileInformationByHandle などのWindows APIを使用する必要があります。
    • クロスプラットフォームなコードを書く場合は、OSごとに異なる実装が必要です。
  • 使用例
    #include <iostream>
    #include <sys/stat.h> // stat, lstat のために必要
    #include <fstream>    // ファイル作成用
    
    int main() {
        const char* path_str = "my_posix_file.txt";
    
        // ファイルを作成
        std::ofstream ofs(path_str);
        ofs.close();
    
        struct stat file_stat;
        // stat はパスを辿り、ファイルの実体の情報を取得
        if (stat(path_str, &file_stat) == 0) { // 成功した場合
            if (S_ISREG(file_stat.st_mode)) { // 通常のファイルかどうかをチェック
                std::cout << path_str << " は通常のファイルです。(POSIX stat)" << std::endl;
            } else {
                std::cout << path_str << " は通常のファイルではありません。(POSIX stat)" << std::endl;
            }
        } else {
            std::cerr << "エラー: stat 関数呼び出しに失敗しました。" << std::endl;
        }
    
        // 後処理
        remove(path_str); // C標準ライブラリのremove
    
        return 0;
    }
    
  • 特徴
    • OSネイティブのAPI。
    • 非常に低レベルで詳細な情報を取得可能。
    • シンボリックリンクの扱いが statlstat で異なる (stat はリンクを辿る、lstat はリンク自体を調べる)。

c. Windows API の GetFileAttributes 関数

Windows では、windows.h ヘッダに含まれる GetFileAttributes 関数を使用してファイル属性を取得できます。

  • 注意点
    • Unix 系OSでは使用できません。
    • ファイルの存在しない場合は INVALID_FILE_ATTRIBUTES を返します。
  • 使用例
    #include <iostream>
    #include <windows.h> // GetFileAttributes のために必要
    #include <fstream>   // ファイル作成用
    
    int main() {
        const char* path_str = "my_win_file.txt";
    
        // ファイルを作成
        std::ofstream ofs(path_str);
        ofs.close();
    
        DWORD attributes = GetFileAttributesA(path_str); // AはANSI版, WはUnicode版
    
        if (attributes != INVALID_FILE_ATTRIBUTES) {
            // 通常のファイルはディレクトリ属性を持たない
            if (!(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
                std::cout << path_str << " は通常のファイルです。(Windows API)" << std::endl;
            } else {
                std::cout << path_str << " は通常のファイルではありません。(Windows API)" << std::endl;
            }
        } else {
            std::cerr << "エラー: GetFileAttributesA 関数呼び出しに失敗しました。" << std::endl;
        }
    
        // 後処理
        remove(path_str);
    
        return 0;
    }
    
  • 特徴
    • Windows OSネイティブのAPI。
    • ファイル属性のビットフラグで情報を取得。

上記以外にも、間接的にファイルが通常のファイルであるかを推測する方法があります。

a. ファイルを開いてみる

最も直接的な方法は、実際にファイルを読み取りモードで開いてみることです。ファイルが正常に開ければ、それは通常のファイルである可能性が高いです。ディレクトリなどは開けません。

  • 注意点
    • ファイルを開く操作は、ファイルシステム操作の中で比較的重い処理です。
    • is_regular_file のように、ファイルの種類(シンボリックリンク、デバイスファイルなど)を明示的に区別することはできません。
  • 使用例
    #include <iostream>
    #include <fstream> // ファイルストリーム
    
    int main() {
        const char* path_str = "my_open_test_file.txt";
    
        // ファイルを作成
        std::ofstream create_ofs(path_str);
        create_ofs.close();
    
        std::ifstream ifs(path_str);
        if (ifs.is_open()) {
            std::cout << path_str << " は通常のファイルとして開くことができました。" << std::endl;
            ifs.close();
        } else {
            std::cerr << path_str << " は通常のファイルとして開けませんでした (ディレクトリ、存在しないなど)。" << std::endl;
        }
    
        // ディレクトリの場合
        // これはC++標準ではディレクトリ作成ができないため、手動で作成しておくか、
        // std::filesystem::create_directory を使ってから試す
        // const char* dir_path_str = "my_open_test_dir";
        // std::ifstream ifs_dir(dir_path_str);
        // if (ifs_dir.is_open()) { ... } else { ... }
    
        // 後処理
        remove(path_str);
    
        return 0;
    }
    
  • 特徴
    • OSやライブラリに依存しない最も基本的な方法。
    • ファイルが本当にアクセス可能であるかのチェックも兼ねる。
    • 開けない場合は、ファイルが存在しない、アクセス権がない、ディレクトリであるなど、様々な理由が考えられるため、エラーメッセージの解釈が必要。
  • C++17 以前
    • Boost.Filesystem
      std::filesystem と同等の機能とインターフェースを求めるなら最適です。
    • OSネイティブAPI (stat, GetFileAttributesなど)
      特定のOSに特化した低レベルな制御が必要な場合や、Boostの依存関係を避けたい場合に検討します。ただし、クロスプラットフォーム対応は自力で行う必要があります。
    • ファイルを開いてみる
      最もシンプルですが、ファイルの種類を厳密に判定するのには向いていません。
  • C++17 以降
    std::filesystem::is_regular_file を使うのが最も推奨される標準的な方法です。クロスプラットフォームで使いやすく、エラーハンドリングも考慮されています。