2010-10-26 20 views
38

मैं नाम सी कार्यक्रमोंकैसे ग में उपयोगकर्ता द्वारा दर्ज स्ट्रिंग

इस मैंने लिखा के लिए उपयोग करते हुए मेरे उपयोगकर्ता द्वारा दर्ज पढ़ना चाहते हैं पढ़ने के लिए:

char name[20]; 

printf("Enter name:"); 
gets(name); 

लेकिन gets का उपयोग कर अच्छा नहीं है तो मेरे सुझाव है एक बेहतर तरीका है।

उत्तर

65

आप चाहिए कभी नहीं उपयोग gets (या एक असीम स्ट्रिंग के आकार के साथ scanf) के बाद से है कि आप अतिप्रवाह बफ़र होना को खोलता है। का उपयोग stdin हैंडल के साथ करें क्योंकि यह आपको अपने बफर में रखे गए डेटा को सीमित करने की अनुमति देता है।

यह एक छोटा सा टुकड़ा मुझे प्रयोक्ता की ओर लाइन इनपुट के लिए उपयोग करें:

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

#define OK  0 
#define NO_INPUT 1 
#define TOO_LONG 2 
static int getLine (char *prmpt, char *buff, size_t sz) { 
    int ch, extra; 

    // Get line with buffer overrun protection. 
    if (prmpt != NULL) { 
     printf ("%s", prmpt); 
     fflush (stdout); 
    } 
    if (fgets (buff, sz, stdin) == NULL) 
     return NO_INPUT; 

    // If it was too long, there'll be no newline. In that case, we flush 
    // to end of line so that excess doesn't affect the next call. 
    if (buff[strlen(buff)-1] != '\n') { 
     extra = 0; 
     while (((ch = getchar()) != '\n') && (ch != EOF)) 
      extra = 1; 
     return (extra == 1) ? TOO_LONG : OK; 
    } 

    // Otherwise remove newline and give string back to caller. 
    buff[strlen(buff)-1] = '\0'; 
    return OK; 
} 

यह मैं अधिकतम आकार सेट कर सकते हैं, अगर बहुत अधिक डेटा लाइन पर दर्ज किया गया है की पहचान करेगा, और बाकी फ्लश लाइन के साथ-साथ यह अगले इनपुट ऑपरेशन को प्रभावित नहीं करता है।

आप की तरह कुछ के साथ यह परीक्षण कर सकते हैं: एक POSIX प्रणाली पर

// Test program for getLine(). 

int main (void) { 
    int rc; 
    char buff[10]; 

    rc = getLine ("Enter string> ", buff, sizeof(buff)); 
    if (rc == NO_INPUT) { 
     // Extra NL since my system doesn't output that on EOF. 
     printf ("\nNo input\n"); 
     return 1; 
    } 

    if (rc == TOO_LONG) { 
     printf ("Input too long [%s]\n", buff); 
     return 1; 
    } 

    printf ("OK [%s]\n", buff); 

    return 0; 
} 
+1

सिस्टम लाइब्रेरीज़ को स्कैनफ को कमांड को ओवरफ़्लो को लागू करने के लिए लागू नहीं करते हैं (मैं समझता हूं कि प्रोग्राम के भीतर यदि डेवलपर इनपुट की जांच नहीं करता है तो ओवरफ़्लो हो सकता है, लेकिन सिस्टम लाइब्रेरी सुरक्षित है?)। – Marm0t

+8

नहीं, यदि आप 20 बाइट बफर में स्कैनफ ("% s") 'हैं और उपयोगकर्ता 40-बाइट लाइन में प्रवेश करता है, तो आप को रोक दिया जाता है। 'स्कैनफ़'' का पूरा बिंदु स्कैन-स्वरूपित है और उपयोगकर्ता इनपुट की तुलना में थोड़ा अधिक _unformatted_ है :-) – paxdiablo

+5

@ मार्म 0t - निम्न प्रश्नों पर विचार करके इस तरह से सोचें: कार्यान्वयन को ओवरफ्लो को कैसे रोक सकता है यदि यह सब मिलता है स्मृति बफर के आकार के बारे में कार्यान्वयन को बताता है कि किसी भी पैरामीटर के बिना स्मृति के एक टुकड़े (एक char * के रूप में टाइपकास्ट) के लिए एक सूचक? –

5

, तो आप शायद getline का उपयोग करना चाहिए, अगर यह उपलब्ध है।

आप चक फाल्कनर के सार्वजनिक डोमेन ggets फ़ंक्शन का भी उपयोग कर सकते हैं जो gets के करीब वाक्यविन्यास प्रदान करता है लेकिन समस्याओं के बिना। (चक फल्कोनर की वेबसाइट अब उपलब्ध नहीं है, हालांकि archive.org has a copy, और मैं my own page for ggets कर दिया है।)

+0

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

+0

@MauryMarkowitz यह एक ऐसी प्रणाली पर काम करेगा जो सीआर को अपने मूल लाइन-एंडिंग प्रारूप के रूप में उपयोग करता है। टेक्स्ट-मोड स्ट्रीम मूल पंक्ति-समाप्ति प्रकार को \ \ n' में परिवर्तित कर देगा। – jamesdlin

15

मुझे लगता है कि उपयोगकर्ता द्वारा दर्ज तार को पढ़ने के लिए सबसे अच्छा और सबसे सुरक्षित तरीका इस्तेमाल कर रहा है getline()

यहाँ एक उदाहरण है कि कैसे करने के लिए ऐसा करते हैं:

#include <stdio.h> 
#include <stdlib.h> 
int main(int argc, char *argv[]) 
{ 
    char *buffer = NULL; 
    int read; 
    unsigned int len; 
    read = getline(&buffer, &len, stdin); 
    if (-1 != read) 
     puts(buffer); 
    else 
     printf("No line read...\n"); 

    printf("Size read: %d\n Len: %d\n", read, len); 
    free(buffer); 
    return 0; 
} 
+1

पढ़ें = getline (और बफर, और लेन, stdin); जीसीसी चेतावनियां देता है जैसे: gcc -Wall -c "getlineEx2.c" getlineEx2.c: फ़ंक्शन मुख्य में: getlineEx2.c: 32: 5: चेतावनी: असंगत सूचक प्रकार से डिफ़ॉल्ट रूप से गेटलाइन के तर्क 2 को पास करना [डिफ़ॉल्ट रूप से सक्षम] पढ़ें = getline (और बफर, और लेन, stdin); ^ फ़ाइल में /usr/include/stdio.h:29te, से getlineEx2.c: 24: /usr/include/sys/stdio.h:37:9: नोट: अपेक्षित आकार_टी * लेकिन तर्क प्रकार के हस्ताक्षर किए गए int * ssize_t _EXFUN (getline, (char **, size_t *, FILE *) प्रकार है; ^ संकलन सफलतापूर्वक समाप्त हुआ। – rpd

+1

बस उस गेटलाइन को अपडेट करने के लिए अब लेंस आकार_टी या अनगिनत लंबे – Wes

+0

डाउनसाइड की आवश्यकता है: POSIX लेकिन एएनएसआई सी नहीं। –

1

आप स्ट्रिंग

scanf("%[^\n]",name); 

पढ़ने के लिए मुझे नहीं पता scanf समारोह का उपयोग कर सकते के बारे में अन्य बेहतर विकल्प स्ट्रिंग प्राप्त करने के लिए,

2

मैं एक आसान और अच्छा समाधान नहीं मिला:

char*string_acquire(char*s,int size,FILE*stream){ 
    int i; 
    fgets(s,size,stream); 
    i=strlen(s)-1; 
    if(s[i]!='\n') while(getchar()!='\n'); 
    if(s[i]=='\n') s[i]='\0'; 
    return s; 
} 

यह fgets पर आधारित है, लेकिन से '\ n' और stdin अतिरिक्त वर्ण (fflush (stdin को बदलने के लिए) मुक्त है कि सभी ओएस पर काम नहीं करता है, अगर आपको इसके बाद स्ट्रिंग हासिल करना है तो उपयोगी)।

0

बीएसडी प्रणालियों और एंड्रॉयड पर आप भी fgetln उपयोग कर सकते हैं:

#include <stdio.h> 

char * 
fgetln(FILE *stream, size_t *len); 

तो जैसा:

size_t line_len; 
const char *line = fgetln(stdin, &line_len); 

line समाप्त हो जाता है नहीं बातिल और \n शामिल (या जो भी आपके प्लेटफ़ॉर्म का उपयोग किया जाता है) में समाप्त। स्ट्रीम पर अगले I/O ऑपरेशन के बाद यह अमान्य हो जाता है। आपको लौटाए गए line बफर को संशोधित करने की अनुमति है।

संबंधित मुद्दे