ファイルの存在を確認する | Programming Place Plus C言語編 逆引き

トップページC言語編逆引き

このページの概要 🔗

以下は目次です。

目的 🔗

ある名前のファイルが、存在しているかどうかを知りたいとします。

現実問題として、このような処理にはあまり信頼性がありません。いくらファイルの存在を調べたところで、調べ終えた直後に、他のプログラムによって、ファイルが作られたり、消されたりする可能性があるからです。

その可能性が排除できないのなら、事前に存在を確認して、その結果に応じて処理を行うよりも、必要なときに普通にファイルを開いたり、作ったりしてみて、その結果に応じて処理を行う方が良いかもしれません。

一応、よく使われる手法を以下に示しますが、 どの方法を選んでも、このような問題には対応できませんし、 他の観点で問題を抱えていることもあります。

方法①(fopen関数を使う) 🔗

よく使われる手法の1つに、 C言語の標準ライブラリに含まれている fopen関数を使うものがあります。

fopen関数の第2引数を “r” にして、読み取りモードでオープンします。このモードの場合、指定したファイルが存在しないと失敗して、ヌルポインタを返すという仕様を利用します。

#include <stdio.h>

/*
    ファイルの存在を確認する。

    path:   ファイルパス。
    戻り値: 存在したら 0以外、存在しなければ 0
*/
int exist_file(const char* path)
{
    FILE* fp = fopen(path, "r");
    if (fp == NULL) {
        return 0;
    }

    fclose(fp);
    return 1;
}

int main(void)
{
    if (exist_file("test.txt")) {
        puts("存在します。");
    }
    else {
        puts("存在しません。");
    }
}

実行結果:

存在します。

この方法の問題は、fopen関数が「指定されたファイルが存在しない」以外の理由でも失敗し得るということです。 たとえば、ファイルの読み取り権限がない場合、読み取りモードでのオープンが行えないため失敗します。

方法②(stat関数を使う)[非標準] 🔗

非標準の関数ですが、 stat関数(→参考。Man page of STAT)を使う方法があります。

#include <stdio.h>
#include <sys/stat.h>

/*
    ファイルの存在を確認する。

    path:   ファイルパス。
    戻り値: 存在したら 0以外、存在しなければ 0
*/
int exist_file(const char* path)
{
    struct stat st;

    if (stat(path, &st) != 0) {
        return 0;
    }

    // ファイルかどうか
    // S_ISREG(st.st_mode); の方がシンプルだが、Visual Studio では使えない。
    return (st.st_mode & S_IFMT) == S_IFREG;
}

int main(void)
{
    if (exist_file("test.txt")) {
        puts("存在します。");
    }
    else {
        puts("存在しません。");
    }
}

実行結果:

存在します。

stat関数は、ファイルの状態を調べる関数です。ファイルに関するさまざまな情報を、stat構造体に格納してもらい、各メンバの値を確認することで、状態を調べられます。

stat関数は成功すると 0 を、エラー発生時には -1 を返します。 このサンプルプログラムでは、どんなエラーでも、ファイルは存在しないものとして扱っていますが、 errno を調べることで、エラーの詳細な内容を判定できます。 ただ、エラーの内容を知ったところで、stat構造体に値を取得できていない以上、 「判定できなかった」という結果を得る程度のことしかできません。

方法③(PathFileExists関数を使う)[Windows]

Windows の場合は、 Windows API の PathFileExists関数(→Microsoft Docs を使用できます。

#include <Shlwapi.h>
#include <stdio.h>

#pragma comment(lib, "Shlwapi.lib")

/*
    ファイルの存在を確認する。

    path:   ファイルパス。
    戻り値: 存在したら 0以外、存在しなければ 0
*/
int exist_file(const char* path)
{
    return PathFileExistsA(path);
}

int main(void)
{
    if (exist_file("test.txt")) {
        puts("存在します。");
    }
    else {
        puts("存在しません。");
    }
}

実行結果:

存在します。

PathFileExists関数を使用するには、Shlwapi.h のインクルードと、Shlwapi.lib のリンクが必要です。
また、PathFileExists は実際にはマクロになっていて、UNICODEマクロの定義の有無によって、 ANSI版(char型)の PathFileExistsA と、Unicode版(wchar_t型)の PathFileExistsW のいずれかに置換されます。 上のサンプルプログラムでは、文字列を const char* で扱っているため、 PathFileExistsA の方を直接呼び出すようにしています。


参考リンク 🔗


更新履歴 🔗

 「NULL」よりも「ヌルポインタ」が適切な箇所について、「ヌルポインタ」に修正。

 「VisualC++」という表現を「VisualStudio」に統一。

 コンパイラの対応状況について、対応している場合は明記しない方針にした。

 clang 3.7 (Xcode 7.3) を、Xcode 8.3.3 に置き換え。

 新規作成。



逆引きのトップページへ

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る