2009-11-29 16 views
5

मैं सी में पता है कि तुम एक स्ट्रिंग और नीचे की तरह वर्णों की संख्या घोषणा कर सकते हैं,सी में चार स्ट्रिंग की लंबाई निर्धारित करने - उपयोगकर्ता स्ट्रिंग की सामग्री आदानों अगर

char mystring[50]; 
साथ '50' वर्णों की संख्या जा रहा है

हालांकि, यदि उपयोगकर्ता स्ट्रिंग की सामग्री इनपुट करने जा रहा है तो उचित प्रक्रिया क्या है (स्कैनफ़ ("% s", mystring) के माध्यम से;)? क्या मैं इसे छोड़ देता हूं,

char mystring[0]; 

इसे '0' के रूप में छोड़कर, क्योंकि मुझे कोई संकेत नहीं है कि उपयोगकर्ता कितने अक्षर इनपुट करेगा?

या मैं करते हैं,

char mystring[400]; 

इनपुट करने के लिए उपयोगकर्ता के लिए 400 अक्षरों तक दे रही है?

उत्तर

6

आपने scanf() और% s के साथ सटीक समस्या पर क्लिक किया है - क्या होता है जब आप नहीं जानते कि कितना इनपुट है?

यदि आप char mystring[0]; चलाने का प्रयास करते हैं, तो आपका प्रोग्राम ठीक से संकलित होगा। लेकिन आप हमेशा segfault करेंगे। आप आकार 0 की सरणी बना रहे हैं, इसलिए जब आप कुछ उस सरणी में रखने का प्रयास करते हैं, तो आप तुरंत अपनी स्ट्रिंग के लिए सीमा से बाहर निकल जाएंगे (क्योंकि कोई स्मृति आवंटित नहीं की जाएगी) - जो एक सेगफॉल्ट है।

तो, बिंदु 1: आपको हमेशा अपनी स्ट्रिंग के लिए आकार आवंटित करना चाहिए। मैं बहुत कम परिस्थितियों के बारे में सोच सकता हूं (ठीक है, कोई नहीं) जहां आप char *mystring के बजाय char mystring[0] कहना चाहेंगे।

अगला, जब आप स्कैनफ़ का उपयोग करते हैं, तो आप कभी भी "% s" विनिर्देशक का उपयोग नहीं करना चाहते हैं - क्योंकि यह स्ट्रिंग के आकार पर कोई सीमा-जांच नहीं करेगा। इसलिए भले ही आपने: उपयोगकर्ता से अधिक 511 वर्ण में प्रवेश करती है

char mystring[512]; 
scanf("%s", mystring); 

अगर (के बाद से 512th \ 0 है), तो आप अपने सरणी के सीमा से बाहर जाना होगा।जिस तरह से इसे ठीक करने के लिए है:

scanf("%511s", mystring); 

यह सब कहना है कि सी स्वचालित रूप से एक स्ट्रिंग आकार बदलने के लिए अगर वहाँ अधिक इनपुट से आप उम्मीद कर रहे हैं एक सुविधा नहीं है है। यह वही चीज है जो आपको मैन्युअल रूप से करना है।

इससे निपटने का एक तरीका fgets() का उपयोग कर है।

आप कह सकते हैं:

while (fgets(mystring, 512, stdin)) 
{ 
    /* process input */ 
} 

फिर आप sscanf() का उपयोग कर सकते लंबाई 5. के एक स्ट्रिंग 4 अक्षर के बाद साथ पार्स करने के लिए mystring

उपरोक्त कोड का प्रयास करें पढ़ लिए गए हैं, कि कोड बाकी इनपुट को पुनः प्राप्त करने के लिए फिर से लूप। "प्रोसेसिंग" में एक बड़ा आकार होने के लिए स्ट्रिंग को फिर से आवंटित करने के लिए कोड शामिल हो सकता है और फिर fgets() से नवीनतम इनपुट जोड़ना शामिल हो सकता है।

उपरोक्त कोड सही नहीं है - यह आपके प्रोग्राम लूप को बनाएगा और किसी भी अनंत स्ट्रिंग लम्बाई को संसाधित करेगा, इसलिए आप उस पर कुछ आंतरिक हार्ड सीमा चाहते हैं (उदाहरण के लिए, अधिकतम 10 बार लूप)।

+0

टी जोड़ा जाना चाहिए कि% s शब्द पढ़ता है, पूरे तार नहीं। चूंकि स्कैनफ प्रारूप स्ट्रिंग रिक्त स्थान और न्यूलाइन को डिलीमीटर के रूप में उपयोग करती है।इस मामले में, इसके बजाए% c का उपयोग करें (फ़ील्ड चौड़ाई के साथ), या जैसा आपने बताया है fgets। फ़ील्ड चौड़ाई के साथ% c के मामले में, संपूर्ण बफर स्ट्रिंग को शून्य पर प्रारंभ करना याद रखें। –

+0

कार्यक्रम हमेशा segfault नहीं होगा। वास्तव में, शायद ज्यादातर समय नहीं। आपका कार्यक्रम शायद चुपचाप टूटा जाएगा। सी सुंदर नहीं है? :-) –

2

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

scanf("%50s", mystring); 

इस मामले में अपने बफर 50 चरित्र क्षेत्र के साथ साथ अशक्त टर्मिनेटर के लिए खाते की 51 अक्षरों का होना चाहिए। या अपने बफर को 50 वर्ण बनाएं और स्कैनफ 49 बताएं चौड़ाई है।

+0

लेकिन स्ट्रिंग घोषित करते समय, क्या मुझे '0' या कुछ बड़ी संख्या निर्दिष्ट करनी चाहिए? – HollerTrain

+1

आपको इस उदाहरण में कम से कम 51 निर्दिष्ट करना चाहिए। (शून्य टर्मिनेटर के लिए लंबाई + 1।) – Thanatos

+0

ठीक है। तो स्ट्रिंग को उचित कोडिंग घोषित करते समय इसे '0' के रूप में सूचीबद्ध कर रहा है? मेरा मुद्दा यह है कि मुझे नहीं पता कि उपयोगकर्ता कितना इनपुट करेगा लेकिन साथ ही सही विधि सीखना चाहता है ... – HollerTrain

2

गेट्स() नामक एक फ़ंक्शन है जो मानक सी लाइब्रेरी का हिस्सा नहीं है। यह एक काफी सरल काम है। यह malloc() का उपयोग कर एक चार सरणी शुरू करता है। फिर यह एक समय में एक char stdin से अक्षर पढ़ता है। यह ट्रैक करता है कि कितने अक्षर पढ़े गए थे और चार सरणी को रीलोक() के साथ विस्तारित करते हैं जब यह अंतरिक्ष से बाहर हो जाता है।

यह यहाँ उपलब्ध है: http://cbfalconer.home.att.net/download/index.htm

मैं तुम्हें कोड पढ़ सकते हैं और अपने आप को फिर से लागू सुझाव है।

0

सी में हमेशा की तरह अभ्यास GNU readline या शायद NetBSD editline, aka libedit. की तरह कुछ का उपयोग करने के

एक सरल या होमवर्क कार्यक्रम के लिए है (एक ही एपीआई, विभिन्न कार्यान्वयन और सॉफ्टवेयर लाइसेंस।), आप सिद्धांत में scanf करने के लिए एक क्षेत्र चौड़ाई दे सकता है , लेकिन एक अधिक सामान्य अभ्यास fgets() को एक निश्चित चौड़ाई सरणी में है और उसके बाद sscanf() चलाएं। इस तरह आप पढ़ने वाली लाइनों की संख्या के नियंत्रण में हैं।

0

उदाहरण के तौर पर, यदि उपयोगकर्ता अपना पहला नाम दर्ज कर रहा है तो आप 'mystring' के आकार को अधिकतम 35 वर्णों के रूप में अधिकतम सुरक्षित नहीं रखते हैं क्योंकि कुछ लोगों के पास वास्तव में लंबे नाम हैं। आप उस मामले तक नहीं पहुंचना चाहते हैं जहां उपयोगकर्ता आपके द्वारा अनुरोधित जानकारी को इनपुट नहीं कर सकता है। ऐसा करने का सही तरीका एक बहुत बड़े आकार के साथ एक अस्थायी बफर होगा जिसमें उपयोगकर्ता द्वारा सभी संभावित इनपुट शामिल होंगे। एक बार जब उपयोगकर्ता जानकारी इनपुट करता है और यह बफर में संग्रहीत हो जाता है तो बफर के अंत में सभी अतिरिक्त स्थान को बंद करते समय वर्णों को बफर से mystring में स्थानांतरित करें। आप 'mystring' के लिए आवश्यक आकार को बताने में सक्षम होंगे और आप इसके लिए केवल उस स्थान की मात्रा को मॉलोक कर सकते हैं और बफर को त्याग सकते हैं। इस तरह आप शेष कार्यक्रम के लिए अधिक मेमोरी का उपयोग करके एक स्ट्रिंग का उपयोग नहीं करेंगे ... आप केवल एक मेमोरी का उपयोग करेंगे जो आपको आवश्यक स्मृति की मात्रा के साथ करेगा।

+0

आपको यह सुनिश्चित करने के लिए अभी भी कुछ प्रकार की जांच करनी होगी कि उपयोगकर्ता इनपुट क्या दुर्लभ मामलों में आवंटित बफर से बड़ा नहीं है या जब कोई आपके प्रोग्राम का शोषण करने का प्रयास कर रहा है। –

1

, तो इसमें कुछ मामूली संशोधनों के साथ cbfalconer के कोड (http://cbfalconer.home.att.net/download/index.htm) और एक फाइल में संकलित किया है:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include "ggets.h" 

#define INITSIZE 112 /* power of 2 minus 16, helps malloc */ 
#define DELTASIZE (INITSIZE + 16) 

enum {OK = 0, NOMEM}; 

int fggets(char* *ln, FILE *f) 
{ 
    int  cursize, ch, ix; 
    char *buffer, *temp; 

    *ln = NULL; /* default */ 
    if (NULL == (buffer = malloc(INITSIZE))) return NOMEM; 
    cursize = INITSIZE; 

    ix = 0; 
    while ((EOF != (ch = getc(f))) && ('\n' != ch)) { 
     if (ix >= (cursize - 1)) { /* extend buffer */ 
     cursize += DELTASIZE; 
     if (NULL == (temp = realloc(buffer, (size_t)cursize))) { 
      /* ran out of memory, return partial line */ 
      buffer[ix] = '\0'; 
      *ln = buffer; 
      return NOMEM; 
     } 
     buffer = temp; 
     } 
     buffer[ix++] = ch; 
    } 
    if ((EOF == ch) && (0 == ix)) { 
     free(buffer); 
     return EOF; 
    } 

    buffer[ix] = '\0'; 
    if (NULL == (temp = realloc(buffer, (size_t)ix + 1))) { 
     *ln = buffer; /* without reducing it */ 
    } 
    else *ln = temp; 
    return OK; 
} /* fggets */ 
/* End of ggets.c */ 

int main(int argc, char **argv) 
{ 
    FILE *infile; 
    char *line; 
    int cnt; 

    //if (argc == 2) 
     //if ((infile = fopen(argv[1], "r"))) { 
     cnt = 0; 
     while (0 == fggets(&line, stdin)) { 
      fprintf(stderr, "%4d %4d\n", ++cnt, (int)strlen(line)); 
      (void)puts(line); 
      free(line); 
     } 
     return 0; 
     //} 
    //(void)puts("Usage: tggets filetodisplay"); 
    //return EXIT_FAILURE; 
} /* main */ 
/* END file tggets.c */ 

मैं इसे बाहर का परीक्षण किया और यह हमेशा आप दे देंगे आप क्या चाहते हैं।

+0

मूल रूप से, अपना मूल कोड प्राप्त करने के लिए आप टिप्पणियों को अपूर्ण करते हैं और fggets कॉल में infile के साथ stdin को प्रतिस्थापित करते हैं। –

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