यदि C में कोई फ़ाइल मौजूद है, तो जाँचने का सबसे अच्छा तरीका क्या है?


436

क्या फ़ाइल खोलने की कोशिश करने से बेहतर तरीका है?

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

मुझे लगता है कि मैं एक्सेस विधि का उत्तर दूंगा, स्टेट विधि एक बहुत ही उचित विकल्प होने के बावजूद, एक्सेस को काम मिल जाता है।
डेव मार्शल

1
क्या आप वास्तव में सिर्फ अस्तित्व की जाँच करना चाहते हैं? या क्या आप जांचना चाहते हैं, और फ़ाइल में लिखें अगर यह पहले से मौजूद नहीं है। यदि हां, तो नीचे दिए गए मेरे जवाब को देखें, एक ऐसे संस्करण के लिए जो दौड़ की स्थिति से ग्रस्त नहीं है।
डैन लेन्स्की

6
मैं नहीं देखता - उस fopen / fclose तरीके से क्या गलत है?
जोहान्स शाउब -

16
@ जोहान्सचैब-लिटब: एक ऐसी चीज़ जो fopen()/ fclose()विधि के साथ गलत है, वह यह है कि आप मौजूद होने के बावजूद पढ़ने के लिए एक फ़ाइल खोलने में सक्षम नहीं हो सकते हैं। उदाहरण के लिए, /dev/kmemमौजूद है, लेकिन अधिकांश प्रक्रियाएं इसे पढ़ने के लिए भी नहीं खोल सकती हैं। /etc/shadowऐसी दूसरी फाइल है। बेशक, दोनों stat()और access()फ़ाइल युक्त निर्देशिका तक पहुंचने में सक्षम होने पर भरोसा करते हैं; यदि आप ऐसा नहीं कर सकते हैं तो सभी दांव बंद हो गए हैं (फ़ाइल युक्त निर्देशिका पर कोई अनुमति निष्पादित नहीं)।
जोनाथन लेफ़लर

1
if (file = fopen(fname, "r"))चेतावनी देगा। इफ-स्टेटमेंट के अंदर विवरण के चारों ओर कोष्ठक का प्रयोग करेंif ((file = fopen(fname, "r")))
जोकिम

जवाबों:


595

access()समारोह में देखो , में पाया unistd.h। आप अपने फ़ंक्शन को बदल सकते हैं

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

तुम भी उपयोग कर सकते हैं R_OK, W_OKऔर X_OKके स्थान पर F_OKपढ़ने की अनुमति, लिखने की अनुमति के लिए जाँच करने के लिए, और नहीं बल्कि अस्तित्व से अनुमति (क्रमशः) पर अमल, और आप उनमें से किसी एक साथ कर सकते हैं या (यानी दोनों पढ़ने के लिए जाँच और लिखने की अनुमति का उपयोग कर R_OK|W_OK)

अद्यतन : ध्यान दें कि विंडोज पर, आप W_OKलिखने की अनुमति के लिए मज़बूती से परीक्षण करने के लिए उपयोग नहीं कर सकते हैं , क्योंकि एक्सेस फ़ंक्शन डीएसीएल को ध्यान में नहीं रखता है। access( fname, W_OK )0 (सफलता) वापस कर सकते हैं क्योंकि फ़ाइल में केवल-पढ़ने के लिए विशेषता सेट नहीं है, लेकिन आपके पास अभी भी फ़ाइल लिखने की अनुमति नहीं हो सकती है।


67
POSIX एक आईएसओ मानक है; यह एक्सेस () को परिभाषित करता है। सी एक और आईएसओ मानक है; ऐसा नहीं होता।
जोनाथन लेफलर

16
पहुँच () के साथ जुड़े नुकसान हैं। एक्सेस () और जो कुछ भी आप बाद में करते हैं, के बीच भेद्यता की एक TOCTOU (चेक का समय, उपयोग का समय) खिड़की है। [... जारी रखा जाए ...]
जोनाथन लेफ्लर

23
[... जारी है ...] POSIX सिस्टम पर अधिक गूढ़ रूप से, प्रभावी UID और प्रभावी GID के बजाय वास्तविक UID और वास्तविक GID, एक्सेस () की जाँच करता है। यह केवल कार्यक्रमों को सेट करने या सेट करने के लिए मायने रखता है, लेकिन फिर यह बहुत ही मायने रखता है क्योंकि यह 'गलत' जवाब दे सकता है।
जोनाथन लेफ्लर

3
जब access()मेरे कोड में कारण टूट गया , तो मैं इस सवाल पर भागा । मैं DevC ++ से CodeBlocks में चला गया और इसने काम करना बंद कर दिया। तो, यह अचूक नहीं है; +1 अधिक @Leffler पर।
बेन

11
अधिकांश समय, हाँ ( access()किसी फ़ाइल के अस्तित्व की जांच के लिए उपयोग करना ठीक है), लेकिन एक SUID या SGID कार्यक्रम में, यहां तक ​​कि गलत भी हो सकता है। यदि परीक्षण की गई फ़ाइल एक ऐसी निर्देशिका में है जो वास्तविक UID या वास्तविक GID तक नहीं पहुंच access()सकती है , तो ऐसी कोई भी फ़ाइल की रिपोर्ट नहीं कर सकती है जब यह मौजूद है। गूढ़ और अनुचित? हाँ।
जोनाथन लेफ़लर

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
@ लुडविगानोरिन: इस तरह की प्रणालियों में, संभावना access()यह है कि समस्याएं भी होती हैं, और बड़ी फ़ाइलों (2 जीबी से बड़ी) के साथ बनाने access()और stat()काम करने के लिए उपयोग करने के लिए विकल्प हैं ।
जोनाथन लेफ़लर

14
क्या आप में से कोई 2 जीबी के बाद की विफलता के बारे में प्रलेखन को इंगित कर सकता है? इसके अलावा, ऐसे मामलों में क्या विकल्प है?
चमकिट्स

@JonathanLeffler statसमान TOCTOU भेद्यता से पीड़ित नहीं है access? (यह मेरे लिए स्पष्ट नहीं है कि यह बेहतर होगा।)
टेलेमाकस

9
दोनों stat()और access()TOCTOU जोखिम से ग्रस्त हैं (ऐसा नहीं करता है lstat(), लेकिन fstat()सुरक्षित है)। यह निर्भर करता है कि आप फ़ाइल की उपस्थिति या अनुपस्थिति के आधार पर क्या करने जा रहे हैं। सही विकल्पों का उपयोग करना open()आमतौर पर समस्याओं से निपटने का सबसे अच्छा तरीका है, लेकिन यह सही विकल्पों को तैयार करने में मुश्किल हो सकता है। ईएएफपी (अनुमति की तुलना में माफी मांगने में आसान) और एलबीवाईएल (लुक बिफोर यू लीप) पर भी चर्चा देखें - उदाहरण के लिए, जावा में एलबीवाईएल बनाम ईएएफपी देखें ।
जोनाथन लेफ़लर

87

आमतौर पर जब आप जांचना चाहते हैं कि क्या कोई फ़ाइल मौजूद है, तो ऐसा इसलिए है क्योंकि आप उस फ़ाइल को बनाना चाहते हैं यदि वह नहीं है। यदि आप उस फ़ाइल को बनाना नहीं चाहते हैं, तो ग्रीम पेरो का उत्तर अच्छा है , लेकिन यदि आप ऐसा करते हैं तो यह एक दौड़ की स्थिति के लिए असुरक्षित है: यदि आप मौजूद हैं, तो एक और प्रक्रिया आपके बीच की फ़ाइल को बना सकती है, और आप वास्तव में इसे लिखने के लिए खोल रहे हैं। । (हंसी मत करो ... यह खराब सुरक्षा प्रभाव हो सकता है अगर बनाई गई फ़ाइल एक सिमलिंक थी!)

यदि आप अस्तित्व की जांच करना चाहते हैं और फ़ाइल बनाना चाहते हैं तो वह मौजूद नहीं है, एटोमिक रूप से ताकि कोई दौड़ की स्थिति न हो, तो इसका उपयोग करें:

#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 का उपयोग करने जा रहे हैं, तो आपको खोलने के लिए तीसरे तर्क के रूप में मोड (अनुमतियाँ) की आपूर्ति करने की आवश्यकता है ()। यह भी विचार करें कि O_TRUNC या O_EXCL या O_APPEND का उपयोग किया जाना चाहिए या नहीं।
जोनाथन लेफलर

6
जोनाथन लेफ़लर सही हैं, इस उदाहरण को लिखित रूप में काम करने के लिए O_EXCL की आवश्यकता है।
रैंडी प्रॉक्टर

6
इसके अलावा, आपको मोड को तीसरे तर्क के रूप में निर्दिष्ट करना होगा: ओपन (लॉक, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR)
andrew cooke

4
यह ध्यान दिया जाना चाहिए कि यह केवल उतना ही सुरक्षित है जितना कि फ़ाइल सिस्टम POSIX आज्ञाकारी है; विशेष रूप से, NFS के पुराने संस्करणों में बहुत दौड़ की स्थिति है O_EXCL को टालना चाहिए था! open(2)लिनक्स पर प्रलेखित, एक वर्कअराउंड है, आपके OS के मैन पेज अलग-अलग हो सकते हैं), लेकिन यह बदसूरत है और दुर्भावनापूर्ण हमलावर के लिए प्रतिरोधी नहीं हो सकता है।
केविन

ध्यान दें कि इस का उपयोग करने के लिए FILE*, आपको fdopen(fd,"flags")FILE*
जेम टेलर

32

हाँ। का उपयोग करें stat()। के लिए मैन पेज देखें stat(2)

stat()यदि फ़ाइल मौजूद नहीं है, तो विफल हो जाएगी, अन्यथा सबसे अधिक संभावना सफल होगी। यदि यह मौजूद है, लेकिन आपके पास उस निर्देशिका तक कोई पहुंच नहीं है जहां यह मौजूद है, तो यह भी विफल हो जाएगा, लेकिन उस स्थिति में कोई भी तरीका विफल हो जाएगा (आप एक निर्देशिका की सामग्री का निरीक्षण कैसे कर सकते हैं जो आप एक्सेस अधिकारों के अनुसार नहीं देख सकते हैं? बस, आप नहीं कर सकते)।

ओह, जैसा कि किसी और ने उल्लेख किया है, आप भी उपयोग कर सकते हैं access()। हालाँकि, मैं पसंद करता हूं stat(), जैसे कि फ़ाइल मौजूद है, तो मुझे तुरंत बहुत सारी उपयोगी जानकारी मिल जाएगी (जब यह अंतिम अपडेट किया गया था, तो यह कितना बड़ा है, स्वामी और / या समूह जो फ़ाइल का मालिक है, पहुंच अनुमतियाँ, और इसी तरह)।


5
यदि आपको केवल फ़ाइल मौजूद है, तो एक्सेस की आवश्यकता होती है। अगर आपको सभी अतिरिक्त जानकारी की आवश्यकता नहीं है, तो स्टेट () में एक बड़ा फेरबदल हो सकता है।
मार्टिन बेकेट

4
वास्तव में जब मैं एक निर्देशिका को ls- कमांड का उपयोग करके सूचीबद्ध करता हूं, तो यह वहां मौजूद होने वाली प्रत्येक फ़ाइल के लिए स्टेट कहता है और यह कि रनिंग ls में एक बड़ा ओवरहेड है, मेरे लिए बहुत नया है। वास्तव में आप हजारों फाइलों के साथ निर्देशिकाओं पर ls चला सकते हैं और यह एक सेकंड के एक अंश में वापस आ जाता है।
मेकी

2
@ मीकी: हार्ड सिस्टम को सपोर्ट करने वाले सिस्टम पर एक्सेस की तुलना में स्टेट में नॉनजरो अतिरिक्त ओवरहेड होता है। इसका कारण यह है कि एक्सेस को केवल डायरेक्टरी एंट्री को देखना है, जबकि स्टेट को इनोड को भी देखना है। खराब चाहने वाले समय (जैसे टेप) के साथ भंडारण उपकरणों पर, अंतर महत्वपूर्ण हो सकता है क्योंकि निर्देशिका प्रविष्टि और इनोड एक दूसरे के बगल में होने की संभावना नहीं है।
केविन

3
@ केविन जब तक आप इसे केवल F_OK पास नहीं करते, तब तक access()किसी फ़ाइल की फ़ाइल एक्सेस अनुमतियों की जांच करता है और ये उस फ़ाइल के लिए इनोड में संग्रहीत हैं और इसकी निर्देशिका प्रविष्टि (कम से कम सभी फ़ाइल सिस्टमों के लिए जो इनोड जैसी संरचनाएँ हैं) में नहीं हैं । इसलिए access()इनोड को उसी तरह stat()एक्सेस करना है जिस तरह से इसे एक्सेस करना है। यदि आप किसी भी अनुमति के लिए जाँच नहीं करते हैं तो आप जो कहते हैं वह केवल सच है! और वास्तव में कुछ प्रणालियों access()पर भी लागू किया जाता है stat()(जैसे कि GNU हर्ड पर glibc इसे इस तरह करता है), इसलिए पहली जगह में कोई गारंटी नहीं है।
मकेरी

@ मीकी: अनुमतियों की जाँच के बारे में किसने कुछ कहा ? मैं विशेष रूप से F_OK के बारे में बात कर रहा था। और हां, कुछ सिस्टम खराब तरीके से लागू होते हैं। एक्सेस कम से कम हर मामले में स्टेट की तरह तेज़ होगा और कुछ समय के लिए तेज़ हो सकता है।
केविन

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
क्या यह स्मृति रिसाव का कारण नहीं होगा? यदि यह मौजूद है तो आप फ़ाइल को कभी भी बंद न करें।
लीजियनममाल 978

1
यह एक अच्छी, सरल विधि है। यदि आप विंडोज एमएसवीसी में हैं, तो इसके बजाय इसका उपयोग करें: (fopen_s(file, "sample.txt", "r"))चूंकि fopen()माना जाता है (या पदावनत त्रुटियों को अक्षम करें, लेकिन इसकी अनुशंसा नहीं की गई है)।
निकोस

15
fopen()मानक C है, यह कहीं नहीं जा रहा है। यह केवल Microsoft द्वारा "पदावनत" है। fopen_s()जब तक आप प्लेटफ़ॉर्म-विशिष्ट, गैर-पोर्टेबल कोड नहीं चाहते, तब तक उपयोग न करें ।
एंड्रयू हेनले

कुछ नहीं के लिए फेकलॉस () कॉलिंग? पहले चर 'फ़ाइल' को निर्दिष्ट करने की आवश्यकता है!
जेनिक्स

1
यहाँ चर 'फ़ाइल' का कचरा मूल्य है। पहली जगह में इसे बंद करने से परेशान क्यों? तुम सिर्फ कॉल कर रहे हैं 'fclose (SOME_RANDOM_ADDRESS);' ..
Jenix

6

विजुअल 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उन स्थितियों में विफल हो सकता है जहां फ़ाइल मौजूद थी लेकिन अनुरोध के अनुसार नहीं खोली जा सकती थी।

संपादित करें: सिर्फ मेकी की पोस्ट पढ़ें। stat()जाने के लिए एक neater रास्ता की तरह लग रहा है। हो हम।


यदि आपको केवल फ़ाइल मौजूद है, तो एक्सेस की आवश्यकता होती है। स्टैड () में एक बड़ा फेर हो सकता है।
मार्टिन बेकेट

4

आप realpath () फ़ंक्शन का उपयोग कर सकते हैं।

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

3

मुझे लगता है कि पहुंच () फ़ंक्शन, जो कि पाया जाता unistd.hहै, Linuxआप के लिए एक अच्छा विकल्प है (आप स्टेट का उपयोग कर सकते हैं भी )।

आप इसे इस तरह से उपयोग कर सकते हैं:

#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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.