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

先頭へ戻る

このエントリーをはてなブックマークに追加

目的

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

現実問題として、このような処理にはあまり信頼性がありません。 いくらファイルの存在を調べたところで、 調べ終えた直後に、他のプログラムによって、 ファイルが作られたり、消されたりする可能性があるからです。
その可能性が排除できないのなら、 事前に存在を確認して、その結果に応じて処理を行うよりも、 必要なときに普通にファイルを開いたり、作ったりしてみて、 その結果に応じて処理を行う方が良いかも知れません。

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

方法@(fopen関数を使う)[C89〜]

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

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

#include <stdio.h>

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

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

	fclose(fp);
	return 1;
}

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

	return 0;
}

実行結果:

存在します。

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

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

非標準の関数ですが、 stat関数(→参考。Man page of STAT)を使う方法があります。 これは、VisualC++、Xcode のいずれでも使用できます。

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

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

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

	if (stat(path, &st) != 0) {
		return 0;
	}
	
	/* ファイルかどうか */
	/* S_ISREG(st.st_mode); の方がシンプルだが、VisualC++ では使えない。 */
	return (st.st_mode & S_IFMT) == S_IFREG;
}

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

	return 0;
}

実行結果:

存在します。

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

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

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

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

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

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

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

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

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

	return 0;
}

実行結果:

存在します。

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


参考リンク

更新履歴

'2017/7/30 clang 3.7 (Xcode 7.3) を、Xcode 8.3.3 に置き換え。

'2017/6/16 新規作成。



逆引きのトップページへ

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

Programming Place Plus のトップページへ