Bir dosyanın C'de olup olmadığını kontrol etmenin en iyi yolu nedir?


436

Sadece dosyayı açmaya çalışmaktan daha iyi bir yol var mı?

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

İstatistik yönteminin çok makul bir alternatif olmasına rağmen, erişim yöntemine cevap vereceğim, erişim işi tamamlıyor.
Dave Marshall

1
Gerçekten sadece varlığını kontrol etmek istiyor musunuz ? Yoksa kontrol etmek ve zaten yoksa dosyaya yazmak mı istiyorsunuz? Eğer öyleyse, yarış koşullarından muzdarip olmayan bir versiyon için aşağıdaki cevabımı görün.
Dan Lenski

6
görmüyorum - bu fopen / fclose yolunda sorun nedir?
Johannes Schaub - litb

16
@ JohannesSchaub-litb: fopen()/ fclose()yöntemiyle ilgili yanlış olan bir şey, bir dosyayı var olmasına rağmen okumak için açamayabileceğinizdir. Örneğin, /dev/kmemvar, ancak çoğu süreç okumak için bile açamıyor. /etc/shadowbaşka bir dosyadır. Tabii ki, her ikisi de stat()ve access()dosyayı içeren dizine erişebilmek güveniyor; bunu yapamazsanız tüm bahisler kapalıdır (dosyayı içeren dizinde yürütme izni yoktur).
Jonathan Leffler

1
if (file = fopen(fname, "r"))bir uyarı verecektir. if ((file = fopen(fname, "r")))
Joakim

Yanıtlar:


595

İçinde bulunan access()işlevi arayın unistd.h. İşlevinizi

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

Ayrıca kullanabilirsiniz R_OK, W_OKve X_OKyerine F_OK(okuma hem kontrol yani okuma izni, yazma izni olup olmadığını kontrol etmek, ve oldukça varlığını daha (sırasıyla) çalıştırma izni ve olabildiğince VEYA birlikte bunlardan herhangi ve kullanan yazma izni R_OK|W_OK)

Güncelleme : W_OKErişim işlevi DACL'leri dikkate almadığı için Windows'ta yazma iznini güvenilir bir şekilde test etmek için kullanamayacağınızı unutmayın. access( fname, W_OK )dosya salt okunur özniteliği olmadığından 0 (başarılı) döndürebilir, ancak yine de dosyaya yazma izniniz olmayabilir.


67
POSIX bir ISO standardıdır; access () işlevini tanımlar. C başka bir ISO standardıdır; o değil.
Jonathan Leffler

16
Access () ile ilişkili tuzaklar var. Access () kullanımı ve daha sonra yaptığınız her şey arasında bir TOCTOU (kontrol zamanı, kullanım zamanı) güvenlik açığı penceresi vardır. [... devamı var ...]
Jonathan Leffler

23
[... devam ediyor ...] POSIX sistemlerinde daha ezoterik olarak access (), etkin UID ve etkili GID yerine gerçek UID ve gerçek GID'nin olup olmadığını kontrol eder. Bu sadece setuid veya setgid programları için önemlidir, ancak daha sonra 'yanlış' cevap verebileceğinden yoğun olarak önemlidir.
Jonathan Leffler

3
Kodumda kırılma nedenini ararken bu soruyla karşılaştım access(). DevC ++ CodeBlocks taşındı ve çalışmayı durdurdu. Yani, yanılmaz değil; @Leffler için +1 daha fazlası.
Ben

11
Çoğu zaman, evet ( access()bir dosyanın varlığını kontrol etmek için kullanmak iyidir ), ancak bir SUID veya SGID programında, bu bile yanlış olabilir. Test edilen dosya gerçek UID veya gerçek GID'nin erişemediği bir dizindeyse access(), mevcut olduğunda böyle bir dosya rapor etmeyebilir. Ezoterik ve ihtimal dışı? Evet.
Jonathan Leffler

116

Bunun statgibi kullanın :

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

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

ve şöyle deyin:

#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: Bu tür sistemlerde, olasılıkları da sorunludur ve büyük dosyalar (2 GB'den büyük) access()yapmak access()ve bunlarla stat()çalışmak için seçenekler vardır .
Jonathan Leffler

14
Her ikiniz de 2 GB'den sonraki arıza ile ilgili belgelere işaret edebilir misiniz? Ayrıca, bu gibi durumlarda alternatif nedir?
chamakits

@JonathanLeffler mu stataynı TOCTOU açığı muzdarip değil access? (Bunun daha iyi olacağı açık değil.)
Telemachus

9
Hem stat()ve access()(yapar TOCTOU açığı muzdarip lstat()ama fstat()güvenlidir). Dosyanın varlığına veya yokluğuna bağlı olarak ne yapacağınıza bağlıdır. Doğru seçenekleri kullanmak open()genellikle sorunlarla başa çıkmanın en iyi yoludur, ancak doğru seçenekleri formüle etmek zor olabilir. EAFP (İzin Vermekten Affetmek Daha Kolay) ve LBYL (Sıçramadan Önce Bak) ile ilgili tartışmalara da bakınız - örneğin Java'da LBYL ve EAFP karşılaştırması .
Jonathan Leffler

87

Genellikle bir dosyanın var olup olmadığını kontrol etmek istediğinizde, bu dosyayı yoksa oluşturmak istersiniz . Graeme Perrow cevabı eğer iyi değil bu dosyayı oluşturmak istiyorum, ancak bunu yaparsanız bu bir yarış durumuna savunmasız: Başka bir işlem, varsa kontrol aradaki dosya oluşturmak olabilir ve aslında o yazmaya açılış . (Gülme ... bu kötü olabilir oluşturulan dosya bir sembolikse bunun güvenlik sonuçları olabilir!)

Varlığını kontrol etmek ve yoksa, atomik olarak herhangi bir yarış koşulu olmaması için dosyayı oluşturmak istiyorsanız, bunu kullanın:

#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 kullanacaksanız, modu (izinler) açmak için üçüncü argüman olarak sağlamanız gerekir (). Ayrıca O_TRUNC veya O_EXCL veya O_APPEND'in kullanılıp kullanılmayacağını düşünün.
Jonathan Leffler

6
Jonathan Leffler haklı, bu örnek O_EXCL'in yazılı olarak çalışmasını gerektiriyor.
Randy Proctor

6
Ayrıca, modu üçüncü bir argüman olarak belirtmeniz gerekir: açık (kilit, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR)
andrew cooke

4
Bunun yalnızca dosya sisteminin POSIX uyumlu olduğu kadar güvenli olduğu unutulmamalıdır; özellikle, NFS'nin eski versiyonları O_EXCL'in kaçınması gereken yarış koşuluna sahip! Belgelenmiş bir çözüm var open(2)(Linux'ta; işletim sisteminizin man sayfaları değişebilir), ancak oldukça çirkin ve kötü niyetli bir saldırgana karşı dirençli olmayabilir.
Kevin

Bunu kullanmak için FILE*, daha sonra fdopen(fd,"flags")oluşturmak için posix yöntemini kullanmanız gerektiğini unutmayınFILE*
Gem Taylor

32

Evet. Kullanın stat(). İçin man sayfasına bakınız stat(2).

stat()dosya yoksa başarısız olur, aksi takdirde büyük olasılıkla başarılı olur. Varsa, ancak bulunduğu dizine okuma erişiminiz yoksa da başarısız olur, ancak bu durumda herhangi bir yöntem başarısız olur (göremediğiniz bir dizinin içeriğini erişim haklarına göre nasıl inceleyebilirsiniz? Basitçe yapamazsınız).

Oh, başka birisinin de belirttiği gibi, kullanabilirsiniz access(). Ancak stat(), dosya varmış gibi hemen bana çok sayıda yararlı bilgi alacaksınız (dosya en son ne zaman güncellendi, dosyanın sahibi, sahibi ve / veya grubu, erişim izinleri vb.).


5
yalnızca dosyanın var olup olmadığını bilmeniz gerekiyorsa erişim engellenir. Tüm ekstra bilgilere ihtiyacınız yoksa Stat () büyük bir kulak misafiri olabilir.
Martin Beckett

4
Aslında ls-command kullanarak bir dizin listelediğimde, orada mevcut olan her dosya için stat çağırır ve çalışan ls büyük bir ek yükü var benim için oldukça yeni. Aslında binlerce dosyayı içeren dizinlerde ls çalıştırabilir ve bir saniyenin bir kısmında döner.
Mecki

2
@Mecki: stat, sabit bağlantıları destekleyen sistemlere kıyasla sıfır olmayan ek yüke sahiptir. Bunun nedeni, erişimin yalnızca dizin girdisine bakması, stat'in de inode'a bakması gerektiğidir. Kötü arama süresine sahip depolama aygıtlarında (örn. Teyp), dizin girişi ve inode'un yan yana olması muhtemel olmadığından fark önemli olabilir.
Kevin

3
@Kevin Yalnızca F_OK access()dosyasını ona aktarmadığınız sürece , bir dosyanın dosya erişim izinlerini denetler ve bunlar o dosyanın inode'unda depolanır ve dizin girdisinde değildir (en azından inode benzeri yapılara sahip tüm dosya sistemleri için) . Bu nedenle access()inode'a tam olarak erişmesi gereken şekilde stat()erişmesi gerekir. Yani söyledikleriniz sadece izinleri kontrol etmezseniz geçerlidir! Ve aslında bazı sistemlerin üzerine access()bile uygulanır stat()(örneğin GNU Hurd üzerinde glibc bunu yapar), bu yüzden ilk etapta garanti yoktur.
Mecki

@Mecki: Kim söyledi şey izinlerini kontrol etmeyle ilgili? Özellikle F_OK hakkında konuşuyordum. Ve evet, bazı sistemler zayıf bir şekilde uygulanmış. Erişim her durumda en az stat kadar hızlı olacaktır ve zaman zaman daha hızlı olabilir.
Kevin

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
Bu bellek sızıntısına neden olmaz mı? Varsa dosyayı asla kapatmazsınız.
LegionMammal978

1
Bu iyi ve basit bir yöntemdir. Windows MSVC'deyseniz, bunu kullanın: (fopen_s(file, "sample.txt", "r"))çünkü fopen()kullanımdan kaldırıldığı kabul edilir (veya kullanımdan kaldırılmış hataları devre dışı bırakın, ancak bu önerilmez).
Nikos

15
fopen()standart C, hiçbir yere gitmiyor. Yalnızca Microsoft tarafından "kullanımdan kaldırılmıştır". fopen_s()Platforma özgü, taşınabilir olmayan kod istemiyorsanız kullanmayın .
Andrew Henle

Hiçbir şey için fclose () mı arıyorsunuz? Önce 'dosya' değişkenini atamanız gerekiyor!
Jenix

1
Buradaki 'file' değişkeninin bir çöp değeri vardır. Neden ilk etapta kapama zahmetine giresin ki? Sadece 'fclose (SOME_RANDOM_ADDRESS)' diyorsunuz; '..
Jenix

6

Visual C ++ yardımından,

/* 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" );
   }
}

Ayrıca, aşağıdaki değerlere dikkat edin :_access(const char *path,int mode)

  • 00: Yalnızca var olma

  • 02: Yazma izni

  • 04: Okuma izni

  • 06: Okuma ve yazma izni

Seninki fopen dosya var ama istendiği gibi açılamadı durumlarda başarısız olabilir.

Düzenleme: Sadece Mecki'nin gönderisini okuyun. stat()gitmek için daha temiz bir yol gibi görünüyor. Ho hum.


yalnızca dosyanın var olup olmadığını bilmeniz gerekiyorsa erişim engellenir. Stat () büyük bir kulak misafiri olabilir.
Martin Beckett

4

Realpath () işlevini kullanabilirsiniz.

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

3

Bence bulunan access () fonksiyonu unistd.hiçin iyi bir seçim Linux( stat de kullanabilirsiniz ).

Bunu şu şekilde kullanabilirsiniz:

#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);
    }
}

Ve aşağıdaki Çıktıyı elde edersiniz:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.