2010-08-16 14 views
21

से पढ़ें मैं एक उपयोगिता लिख ​​रहा हूं जो फ़ाइल नाम स्वीकार करता है, या stdin से पढ़ता है।फ़ाइल या stdin

मैं यह देखने के लिए सबसे मजबूत/सबसे तेज़ तरीका जानना चाहता हूं कि क्या स्टडीन मौजूद है (डेटा को डेटा पर पाइप किया जा रहा है) और यदि ऐसा डेटा पढ़ रहा है। यदि यह अस्तित्व में नहीं है, तो प्रसंस्करण दिए गए फ़ाइल नाम पर रखें। मैंने stdin के आकार के लिए निम्न परीक्षण का उपयोग करने का प्रयास किया है, लेकिन मुझे विश्वास है क्योंकि यह एक स्ट्रीम है और वास्तविक फ़ाइल नहीं है, यह काम नहीं कर रहा है क्योंकि मुझे संदेह है कि यह होगा और यह हमेशा -1 प्रिंट कर रहा है। मुझे पता है कि मैं हमेशा एक समय में इनपुट 1 चरित्र पढ़ सकता हूं! = ईओएफ लेकिन मुझे एक और सामान्य समाधान चाहिए, इसलिए यदि मैं stdin मौजूद हूं तो मैं या तो एफडी या फ़ाइल * के साथ समाप्त कर सकता हूं ताकि शेष कार्यक्रम निर्बाध रूप से कार्य करेगा । मैं अपने आकार को जानने में सक्षम होना चाहूंगा, लंबित कार्यक्रम पिछले कार्यक्रम द्वारा बंद कर दिया गया है।

long getSizeOfInput(FILE *input){ 
    long retvalue = 0; 
    fseek(input, 0L, SEEK_END); 
    retvalue = ftell(input); 
    fseek(input, 0L, SEEK_SET); 
    return retvalue; 
} 

int main(int argc, char **argv) { 
    printf("Size of stdin: %ld\n", getSizeOfInput(stdin)); 
    exit(0); 
} 

टर्मिनल:

$ echo "hi!" | myprog 
Size of stdin: -1 

उत्तर

15

सबसे पहले, प्रोग्राम को यह पूछने के लिए कहें कि errno, जो विफलता पर सेट है, जैसे fseek या ftell के दौरान गलत है।

अन्य (tonio & लैटिनसुड) ने फ़ाइल नाम के लिए स्टडीन बनाम जांच के साथ गलती की व्याख्या की है। अर्थात्, नामक एक विशेष मामले के रूप में - का इलाज करने के लिए if (argc > 1) निर्दिष्ट किसी भी कमांड लाइन पैरामीटर को देखने के लिए पहले argc (तर्क गणना) देखें।

कोई पैरामीटर निर्दिष्ट कर रहे हैं, तो इनपुट (जा रहा) है stdin से आने के लिए है, जो एक धारा है फ़ाइल नहीं है, और fseek समारोह उस पर विफल रहता है मान लेते हैं।

एक धारा है, तो आप फ़ाइल-ऑन-डिस्क उन्मुख पुस्तकालय कार्यों (यानी fseek और ftell) का उपयोग नहीं कर सकते हैं, तो आप बस है (newline वर्ण अनुगामी सहित) पढ़े गए बाइट की संख्या की गणना करना के मामले में प्राप्त EOF तक (फ़ाइल का अंत)।

बड़ी फ़ाइलों के साथ उपयोग के लिए आप एक (पाठ) फ़ाइल में बाइट्स के अधिक कुशल पढ़ने के लिए fgets को चार सरणी में उपयोग करके इसे तेज कर सकते हैं। एक बाइनरी फ़ाइल के लिए आपको fopen(const char* filename, "rb") का उपयोग करने और fgetc/fgets के बजाय fread का उपयोग करने की आवश्यकता है।

स्ट्रीम से पढ़ने पर किसी भी त्रुटि का पता लगाने के लिए बाइट-गिनती विधि का उपयोग करते समय आप feof(stdin)/ferror(stdin) के लिए भी जांच सकते हैं।

नीचे नमूना सी 99 अनुपालन और पोर्टेबल होना चाहिए।

#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 

long getSizeOfInput(FILE *input){ 
    long retvalue = 0; 
    int c; 

    if (input != stdin) { 
     if (-1 == fseek(input, 0L, SEEK_END)) { 
     fprintf(stderr, "Error seek end: %s\n", strerror(errno)); 
     exit(EXIT_FAILURE); 
     } 
     if (-1 == (retvalue = ftell(input))) { 
     fprintf(stderr, "ftell failed: %s\n", strerror(errno)); 
     exit(EXIT_FAILURE); 
     } 
     if (-1 == fseek(input, 0L, SEEK_SET)) { 
     fprintf(stderr, "Error seek start: %s\n", strerror(errno)); 
     exit(EXIT_FAILURE); 
     } 
    } else { 
     /* for stdin, we need to read in the entire stream until EOF */ 
     while (EOF != (c = fgetc(input))) { 
     retvalue++; 
     } 
    } 

    return retvalue; 
} 

int main(int argc, char **argv) { 
    FILE *input; 

    if (argc > 1) { 
     if(!strcmp(argv[1],"-")) { 
     input = stdin; 
     } else { 
     input = fopen(argv[1],"r"); 
     if (NULL == input) { 
      fprintf(stderr, "Unable to open '%s': %s\n", 
        argv[1], strerror(errno)); 
      exit(EXIT_FAILURE); 
     } 
     } 
    } else { 
     input = stdin; 
    } 

    printf("Size of file: %ld\n", getSizeOfInput(input)); 

    return EXIT_SUCCESS; 
} 
0

बस feof के साथ फ़ाइल के अंत करना होगा के लिए परीक्षण, मुझे लगता है।

+1

'feof' मुश्किल है क्योंकि यह आपको स्ट्रीम से पढ़ने और विफल होने का पूर्व प्रयास करने की आवश्यकता है। (और यदि आप ऐसा करते हैं, तो आप विफलता कारण की जांच भी कर सकते हैं।) यह स्पष्ट नहीं है कि आप इस स्थिति के लिए इसका उपयोग कैसे करते हैं। – jamesdlin

5

उदाहरण के लिए, आप cat उपयोगिता में यह कैसे देख सकते हैं।

कोड here देखें। यदि कोई फ़ाइल नाम तर्क के रूप में नहीं है, या यह "-" है, तो stdin इनपुट के लिए उपयोग किया जाता है। stdin वहां होगा, भले ही कोई डेटा धक्का न दिया जाए (लेकिन फिर, आपका पठन कॉल हमेशा के लिए इंतजार कर सकता है)।

4

आप तब तक स्टडीन से पढ़ सकते हैं जब तक कि उपयोगकर्ता फ़ाइल नाम की आपूर्ति न करे?

यदि नहीं, तो "stdin से पढ़ें" के रूप में विशेष "फ़ाइल नाम" - का इलाज करें। उपयोगकर्ता को cat file | myprogram - जैसे प्रोग्राम को शुरू करना होगा यदि वह डेटा को पाइप करना चाहता है, और myprogam file यदि वह फ़ाइल से पढ़ना चाहता है।

int main(int argc,char *argv[]) { 
    FILE *input; 
    if(argc != 2) { 
    usage(); 
    return 1; 
    } 
    if(!strcmp(argv[1],"-")) { 
    input = stdin; 
    } else { 
     input = fopen(argv[1],"rb"); 
     //check for errors 
    } 

आप * nix पर हैं, तो आप देख सकते हैं कि stdin एक फीफो है:

struct stat st_info; 
if(fstat(0,&st_info) != 0) 
    //error 
    } 
    if(S_ISFIFO(st_info.st_mode)) { 
    //stdin is a pipe 
    } 

हालांकि कर रही उपयोगकर्ता को संभाल नहीं होगा myprogram <file

तुम भी अगर जाँच कर सकते हैं stdin एक टर्मिनल/कंसोल

if(isatty(0)) { 
    //stdin is a terminal 
} 
21

आप इसे गलत सोच रहे हैं।

तुम क्या करने कोशिश कर रहे हैं:

stdin इसका इस्तेमाल मौजूद है, तो किसी और उपयोगकर्ता एक फ़ाइल नाम की आपूर्ति की जांच। उपयोगकर्ता एक फ़ाइल नाम की आपूर्ति

है, तो फ़ाइल नाम का उपयोग:

आप के बजाय क्या करना चाहिए क्या

। अन्यथा stdin का उपयोग करें।

आप आने वाली स्ट्रीम की कुल लंबाई नहीं जान सकते हैं जबतक कि आप इसे सब कुछ नहीं पढ़ते और इसे buffered रखते हैं। आप बस पाइप में पीछे की ओर नहीं खोज सकते हैं। यह एक सीमा है कि पाइप कैसे काम करते हैं।पाइप सभी कार्यों के लिए उपयुक्त नहीं हैं और कभी-कभी मध्यवर्ती फाइलों की आवश्यकता होती है।

0

ध्यान दें कि क्या आप चाहते हैं जानना चाहते हैं कि stdin या नहीं एक टर्मिनल से जुड़ा हुआ है, यदि वह मौजूद नहीं। यह हमेशा मौजूद होता है लेकिन जब आप इसमें पाइप करने के लिए खोल का उपयोग करते हैं या फ़ाइल पढ़ते हैं, तो यह टर्मिनल से कनेक्ट नहीं होता है।

आप देख सकते हैं कि एक फ़ाइल वर्णनकर्ता termios.h कार्यों के माध्यम से एक टर्मिनल से जुड़ा है:

#include <termios.h> 
#include <stdbool.h> 

bool stdin_is_a_pipe(void) 
{ 
    struct termios t; 
    return (tcgetattr(STDIN_FILENO, &t) < 0); 
} 

यह stdin के टर्मिनल गुण लाने की कोशिश करेंगे। यदि यह किसी पाइप से कनेक्ट नहीं है, तो यह एक tty से जुड़ा हुआ है और tcgetattr फ़ंक्शन कॉल सफल हो जाएगा। एक पाइप का पता लगाने के लिए, हम tcgetattr विफलता की जांच करते हैं।

+0

को STDIN_FILENO परिभाषित करने के लिए # शामिल जोड़ना पड़ा –