Cにファイルが存在するかどうかを確認する最良の方法は何ですか?


436

単にファイルを開こうとするよりも良い方法はありますか?

int exists(const char *fname)
{
    FILE *file;
    if ((file = fopen(fname, "r")))
    {
        fclose(file);
        return 1;
    }
    return 0;
}

statメソッドが非常に合理的な代替手段であるにもかかわらず、アクセスメソッドに答えを出そうと思います。
デイブマーシャル

1
あなたは、ください本当にただ存在するかどうかを確認したいですか?または、チェックして、ファイルがまだ存在しない場合はファイルに書き込みますか?もしそうなら、競合状態に悩まされないバージョンについては、以下の私の答えを見てください。
Dan Lenski

6
私は見ません-そのfopen / fclose方法の何が問題になっていますか?
Johannes Schaub-litb 2009

16
@ JohannesSchaub-litb:fopen()/ fclose()メソッドの1つの誤りは、ファイルが存在していても、ファイルを開いて読み取ることができない場合があることです。たとえば、/dev/kmem存在しますが、ほとんどのプロセスは読み取りのためにもそれを開くことができません。 /etc/shadow別のそのようなファイルです。もちろん、両方stat()access()ファイルを含むディレクトリにアクセスできることに依存しています。それができない場合は、すべての賭けが無効になります(ファイルを含むディレクトリに対する実行権限がないため)。
ジョナサンレフラー2013年

1
if (file = fopen(fname, "r"))警告が表示されます。ifステートメント内のステートメントを括弧if ((file = fopen(fname, "r")))
Joakim

回答:


595

にあるaccess()関数を調べunistd.hます。関数を次のように置き換えることができます

if( access( fname, F_OK ) != -1 ) {
    // file exists
} else {
    // file doesn't exist
}

の代わりに、、、およびを使用してR_OK、存在するのではなく、読み取り権限、書き込み権限、および実行権限を(それぞれ)確認し、それらのORを実行できます(つまり、を使用して、読み取り権限書き込み権限の両方を確認します)W_OKX_OKF_OKR_OK|W_OK

更新:Windowsでは、W_OKアクセス機能がDACLを考慮しないため、を使用して書き込み権限を確実にテストできないことに注意してください。access( fname, W_OK )ファイルに読み取り専用の属性が設定されていないため、0(成功)を返すことがありますが、ファイルへの書き込み権限がない可能性があります。


67
POSIXはISO標準です。access()を定義します。Cは別のISO標準です。ありません。
Jonathan Leffler、

16
access()に関連する落とし穴があります。access()の使用とその後に行うことの間には、脆弱性のTOCTOU(チェックの時間、使用の時間)ウィンドウがあります。[続きます...]
ジョナサンレフラー、

23
[...継続...]どちらかと言えば、POSIXシステムでは、access()は実効UIDと実効GIDではなく、実際のUIDと実際のGIDかどうかをチェックします。これはsetuidまたはsetgidプログラムにのみ関係しますが、「間違った」答えを出す可能性があるため、非常に重要です。
Jonathan Leffler、

3
access()私のコードで壊れた理由を探しているとき、私はこの質問に出くわしました。DevC ++からCodeBlocksに移行したところ、機能しなくなりました。ですから、それは間違いのないことではありません。@Lefflerにさらに+1します。
Ben

11
ほとんどの場合、はい(access()ファイルの存在を確認するために使用しても問題ありません)ですが、SUIDまたはSGIDプログラムでは、それが正しくない場合もあります。テストされたファイルが、実際のUIDまたは実際のGIDがアクセスできないaccess()ディレクトリにある場合、そのファイルが存在する場合、そのようなファイルは報告されない可能性があります。難解でありそうにない?はい。
ジョナサンレフラー2013

116

statこのように使用してください:

#include <sys/stat.h>   // stat
#include <stdbool.h>    // bool type

bool file_exists (char *filename) {
  struct stat   buffer;   
  return (stat (filename, &buffer) == 0);
}

次のように呼び出します:

#include <stdio.h>      // printf

int main(int ac, char **av) {
    if (ac != 2)
        return 1;

    if (file_exists(av[1]))
        printf("%s exists\n", av[1]);
    else
        printf("%s does not exist\n", av[1]);

    return 0;
}

4
@LudvigANorinは:そのようなシステムで、チャンスがあることですaccess()また、問題がある、と作るために使用するためのオプションがありますaccess()し、stat()大きなファイル(2ギガバイトよりも大きい)との仕事は。
ジョナサンレフラー2013年

14
どちらかが2 GB後の障害に関するドキュメントを参照できますか?また、そのような場合の代替案は何ですか?
chamakits 2013

@JonathanLeffler statと同じTOCTOUの脆弱性の影響を受けaccessませんか?(それがより良いかどうかは私には明らかではありません。)
テレマコス

9
どちらstat()access()(そうTOCTOUの脆弱性に苦しむlstat()が、fstat()安全です)。これは、ファイルの有無に基づいて何をするかによって異なります。正しいオプションを使用することopen()は、通常、問題に対処する最良の方法ですが、正しいオプションを作成するのは難しい場合があります。EAFP(許可よりも許しを求める方が簡単)とLBYL(Look Before You Leap)に関する議論もご覧ください- たとえば、JavaでのLBYLとEAFPを参照してください。
ジョナサンレフラー、2014年

87

通常、ファイルが存在するかどうかを確認するときは、存在しない場合にそのファイルを作成するためです。 そのファイルを作成したくない場合、Graeme Perrowの回答は適切ですが、作成すると競合状態に対して脆弱になります。別のプロセスが、ファイルが存在するかどうかを確認し、実際に開いて書き込むためにファイルを作成する可能性があります。 。(笑わないでください... 作成されたファイルがシンボリックリンクの場合、これはセキュリティに影響を及ぼす可能性があります!)

存在を確認し、存在しない場合はファイル作成する場合は、アトミックに競合状態が発生しないようにしてから、これを使用します。

#include <fcntl.h>
#include <errno.h>

fd = open(pathname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0) {
  /* failure */
  if (errno == EEXIST) {
    /* the file already existed */
    ...
  }
} else {
  /* now you can use the file */
}

8
O_CREATを使用する場合は、open()の3番目の引数としてモード(権限)を指定する必要があります。また、O_TRUNC、O_EXCL、またはO_APPENDのどちらを使用するかを検討してください。
Jonathan Leffler、

6
Jonathan Lefflerは正解です。この例では、O_EXCLが記述どおりに機能する必要があります。
ランディプロクター

6
また、3番目の引数としてモードを指定する必要があります
。open

4
これは、ファイルシステムがPOSIXに準拠している場合にのみ安全であることに注意してください。特に、NFSの古いバージョンには、O_EXCLが回避することになっていた非常に競合状態があります!に記載されている回避策がありopen(2)ます(Linuxでは、OSのマニュアルページは異なる場合があります)。
ケビン

これをFILE*で使用するには、posixメソッドfdopen(fd,"flags")を使用してFILE*
Gem Taylor

32

はい。を使用しstat()ます。のマニュアルページを参照してくださいstat(2)

stat()ファイルが存在しない場合は失敗し、そうでない場合はおそらく成功します。存在しているが、存在するディレクトリへの読み取りアクセス権がない場合も失敗しますが、その場合はどのメソッドも失敗します(アクセス権に応じて表示されないディレクトリの内容をどのように検査できますか?単に、できません)。

ああ、誰かが言ったように、あなたも使うことができますaccess()。ただしstat()、ファイルが存在する場合、すぐに多くの有用な情報(最後に更新されたのはいつか、ファイルの大きさ、ファイルを所有する所有者やグループ、アクセス許可など)が得られるため、私はを好みます。


5
ファイルが存在するかどうかを知るだけでよい場合は、アクセスが優先されます。すべての追加情報が必要ない場合、Stat()は大きな耳を傾ける可能性があります。
マーティンベケット

4
実際、ls-commandを使用してディレクトリを一覧表示すると、そこに存在するすべてのファイルに対してstatが呼び出され、lsを実行すると大きなオーバーヘッドが発生することは、私にとっては初めてです。実際、何千ものファイルがあるディレクトリでlsを実行すると、ほんの一瞬で戻ります。
メッキー

2
@Mecki:ハードリンクをサポートするシステムでのアクセスと比較して、statにはゼロ以外の追加オーバーヘッドがあります。これは、アクセスがディレクトリエントリを参照するだけでよく、statがiノードも参照する必要があるためです。シーク時間の悪いストレージデバイス(テープなど)では、ディレクトリエントリとiノードが互いに隣接する可能性が低いため、違いが大きくなる可能性があります。
ケビン

3
@Kevin F_OKのみを渡さない限りaccess()、ファイルのファイルアクセス許可をチェックし、これらはそのファイルのiノードに格納され、そのディレクトリエントリにはありません(少なくともiノードのような構造を持つすべてのファイルシステム)。 。そのaccess()ため、iノードにアクセスするのとまったく同じ方法でiノードstat()にアクセスする必要があります。だから、あなたが言うことは、権限をチェックしない場合にのみ当てはまります!そして実際には、一部のシステムでaccess()はさらにその上に実装されていますstat()(たとえば、GNU Hurdのglibcはそのようにしています)。そのため、そもそも保証はありません。
Mecki

@Mecki:言った何もパーミッションをチェックについては?特にF_OKについて話していました。そして、はい、一部のシステムは不十分に実装されています。すべての場合において、アクセスは少なくともstatと同じくらい高速であり、場合によってはより高速になることがあります。
ケビン

9
FILE *file;
    if((file = fopen("sample.txt","r"))!=NULL)
        {
            // file exists
            fclose(file);
        }
    else
        {
            //File not found, no memory leak since 'file' == NULL
            //fclose(file) would cause an error
        }

1
これにより、メモリリークが発生しませんか?ファイルが存在する場合は、決して閉じないでください。
LegionMammal978 2016年

1
これは優れたシンプルな方法です。Windows MSVCを使用している場合は、代わりにこれを使用します。は非推奨と見なされている(fopen_s(file, "sample.txt", "r"))ためfopen()です(または非推奨のエラーを無効にしますが、お勧めしません)。
ニコス2017

15
fopen()標準Cですが、どこにも行きません。これは、Microsoftによってのみ「非推奨」です。fopen_s()プラットフォーム固有で移植性のないコードが必要でない限り、使用しないでください。
Andrew Henle 2017年

何もせずにfclose()を呼び出していますか?最初に変数「ファイル」を割り当てる必要がありました!
Jenix 2017

1
ここでの変数「ファイル」にはガベージ値があります。そもそもなぜそれを閉じる必要があるのですか?あなただけの'(SOME_RANDOM_ADDRESS)fcloseを;'呼んでいる...
Jenix

6

Visual C ++ヘルプから、私は一緒に行く傾向があります

/* ACCESS.C: This example uses _access to check the
 * file named "ACCESS.C" to see if it exists and if
 * writing is allowed.
 */

#include  <io.h>
#include  <stdio.h>
#include  <stdlib.h>

void main( void )
{
   /* Check for existence */
   if( (_access( "ACCESS.C", 0 )) != -1 )
   {
      printf( "File ACCESS.C exists\n" );
      /* Check for write permission */
      if( (_access( "ACCESS.C", 2 )) != -1 )
         printf( "File ACCESS.C has write permission\n" );
   }
}

また、次のモード値にも注意してください。_access(const char *path,int mode)

  • 00:存在のみ

  • 02:書き込み許可

  • 04:読み取り許可

  • 06:読み取りおよび書き込み権限

fopenファイルが存在していても、要求どおりに開けない状況では失敗する可能性があるためです。

編集:Meckiの投稿を読んでください。 stat()行くのにきちんとした方法のように見えます。ほら。


ファイルが存在するかどうかを知るだけでよい場合は、アクセスが優先されます。Stat()は大きな耳にすることがあります。
Martin Beckett、

4

realpath()関数を使用できます。

resolved_file = realpath(file_path, NULL);
if (!resolved_keyfile) {
   /*File dosn't exists*/
   perror(keyfile);
   return -1;
}

3

にあるaccess()関数unistd.hLinuxstatも使用できます)に適しています。

次のように使用できます。

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>

void fileCheck(const char *fileName);

int main (void) {
    char *fileName = "/etc/sudoers";

    fileCheck(fileName);
    return 0;
}

void fileCheck(const char *fileName){

    if(!access(fileName, F_OK )){
        printf("The File %s\t was Found\n",fileName);
    }else{
        printf("The File %s\t not Found\n",fileName);
    }

    if(!access(fileName, R_OK )){
        printf("The File %s\t can be read\n",fileName);
    }else{
        printf("The File %s\t cannot be read\n",fileName);
    }

    if(!access( fileName, W_OK )){
        printf("The File %s\t it can be Edited\n",fileName);
    }else{
        printf("The File %s\t it cannot be Edited\n",fileName);
    }

    if(!access( fileName, X_OK )){
        printf("The File %s\t is an Executable\n",fileName);
    }else{
        printf("The File %s\t is not an Executable\n",fileName);
    }
}

そして、あなたは次の出力を取得します:

The File /etc/sudoers    was Found
The File /etc/sudoers    cannot be read
The File /etc/sudoers    it cannot be Edited
The File /etc/sudoers    is not an Executable
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.