आप स्कैनफ़ का उपयोग करके रिक्त स्थान कैसे दर्ज कर सकते हैं?


129

निम्नलिखित कोड का उपयोग करना:

char *name = malloc(sizeof(char) + 256); 

printf("What is your name? ");
scanf("%s", name);

printf("Hello %s. Nice to meet you.\n", name);

एक उपयोगकर्ता अपना नाम दर्ज कर सकता है, लेकिन जब वे किसी स्थान के साथ नाम दर्ज करते हैं Lucas Aardvark, scanf()जैसे बाद में सब कुछ काट दिया जाता है Lucas। मैं scanf()रिक्त स्थान कैसे बनाऊं


9
ध्यान दें कि अधिक मुहावरेदार है 'मॉलॉक (आकार (चार) * 256 + 1)', या 'मॉलोक (256 + 1)', या इससे भी बेहतर (स्थानीय रूप से 'नाम' का सख्ती से इस्तेमाल किया जाएगा) 'चार नाम [256 + 1] ] '। '+1' शून्य टर्मिनेटर के लिए एक न्यूमोनिक के रूप में कार्य कर सकता है, जिसे आवंटन में शामिल करने की आवश्यकता है।
बैरी केली

@ बेरी - मुझे लगता है sizeof(char) + 256कि एक टाइपो था।
क्रिस लुत्ज़

जवाबों:


186

लोग (और विशेष रूप से शुरुआती) का उपयोग नहीं किया जाना चाहिए scanf("%s")या gets()या किसी अन्य कार्यों कि बफर अतिप्रवाह सुरक्षा की जरूरत नहीं है, जब तक आप कुछ के लिए पता है कि इनपुट हमेशा एक विशिष्ट प्रारूप (और शायद तब भी नहीं) के लिए किया जाएगा।

याद रखें scanf"स्कैन स्वरूपित" के लिए खड़ा है और उपयोगकर्ता द्वारा दर्ज किए गए डेटा की तुलना में बहुत कम कीमती है । यह आदर्श है यदि आपके पास इनपुट डेटा प्रारूप का कुल नियंत्रण है, लेकिन आमतौर पर उपयोगकर्ता इनपुट के लिए अनुपयुक्त है।

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

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

/* Maximum name size + 1. */

#define MAX_NAME_SZ 256

int main(int argC, char *argV[]) {
    /* Allocate memory and check if okay. */

    char *name = malloc(MAX_NAME_SZ);
    if (name == NULL) {
        printf("No memory\n");
        return 1;
    }

    /* Ask user for name. */

    printf("What is your name? ");

    /* Get the name, with size limit. */

    fgets(name, MAX_NAME_SZ, stdin);

    /* Remove trailing newline, if there. */

    if ((strlen(name) > 0) && (name[strlen (name) - 1] == '\n'))
        name[strlen (name) - 1] = '\0';

    /* Say hello. */

    printf("Hello %s. Nice to meet you.\n", name);

    /* Free memory and exit. */

    free (name);
    return 0;
}

1
मैं इसके बारे में नहीं जानता था fgets()। यह वास्तव में उपयोग करने के लिए आसान लग रहा है scanf()। +1
Kredns

7
यदि आप केवल उपयोगकर्ता से एक पंक्ति प्राप्त करना चाहते हैं, तो यह आसान है। यह भी सुरक्षित है क्योंकि आप बफर ओवरफ्लो से बच सकते हैं। स्कैनफ़ परिवार अलग-अलग चीजों में एक स्ट्रिंग को मोड़ने के लिए वास्तव में उपयोगी है (जैसे चार वर्ण और "% c% c% c% c% d" के साथ उदाहरण के लिए) लेकिन, फिर भी, आपको फ़ॉइट और sscanf का उपयोग करना चाहिए, न कि स्कैनर, बफर अतिप्रवाह की संभावना से बचने के लिए।
paxdiablo

4
आप स्कैनफ प्रारूप में अधिकतम बफर आकार रख सकते हैं, आप रनटाइम पर प्रारूप के निर्माण के बिना रनटाइम गणना नहीं कर सकते हैं (प्रिंटफ के लिए * के बराबर नहीं है, * एक अन्य व्यवहार के साथ स्कैनफ के लिए एक वैध मापक है: असाइनमेंट को दबाना )।
एपीग्रामग्राम

यह भी ध्यान रखें कि scanf(यदि सांख्यिक रूपांतरण overflows अपरिभाषित व्यवहार है N1570 7.21.6.2p10 , अंतिम वाक्य में इन शब्दों की C89 के बाद से नहीं बदला गया) जिसका अर्थ है कोई भी के scanfकार्यों को सुरक्षित रूप से अविश्वसनीय इनपुट के संख्यात्मक रूपांतरण के लिए इस्तेमाल किया जा सकता।
18

@JonathanKomar और भविष्य में इसे पढ़ने वाले अन्य व्यक्ति: यदि आपके प्रोफेसर ने आपको बताया था कि आपको scanfएक असाइनमेंट में उपयोग करना है, तो वे ऐसा करने के लिए गलत थे, और आप उन्हें बता सकते हैं कि मैंने ऐसा कहा था, और यदि वे मेरे साथ बहस करना चाहते हैं , मेरा ईमेल पता मेरे प्रोफ़ाइल से आसानी से मिल जाता है।
zwol

124

प्रयत्न

char str[11];
scanf("%10[0-9a-zA-Z ]", str);

उम्मीद है की वो मदद करदे।


10
(1) स्पष्ट रूप से रिक्त स्थान स्वीकार करने के लिए, आपको चरित्र वर्ग में एक स्थान रखने की आवश्यकता है। (2) ध्यान दें कि 10 अधिकतम वर्ण हैं जिन्हें पढ़ा जाएगा, इसलिए str को कम से कम 11 आकार के बफर की ओर इंगित करना होगा। (3) अंतिम s यहाँ एक प्रारूप निर्देश नहीं है, लेकिन स्कैनफ यह पूरी तरह से मिलान करने के लिए यहाँ कोशिश करेगा। इसका प्रभाव 1234567890s की प्रविष्टि पर दिखाई देगा जहाँ s का उपभोग किया जाएगा लेकिन जहाँ नहीं रखा गया है। एक अन्य पत्र का उपभोग नहीं किया जाएगा। यदि आप s के बाद एक और फॉर्मेट डालते हैं, तो यह तभी पढ़ा जाएगा जब मिलान किया जाना है।
एपीग्रामग्राम

एक अन्य संभावित समस्या है, पहली या आखिरी की तुलना में अन्य जगह पर उपयोग को परिभाषित किया गया है। आमतौर पर, इसका उपयोग श्रेणियों के लिए किया जाता है, लेकिन रेंज पदनाम क्या चारसेट पर निर्भर है। EBCDIC में अक्षर की श्रेणी में छेद होते हैं और यहां तक ​​कि जब एक ASCII व्युत्पन्न वर्ण ग्रहण करते हैं, तो यह सोचना भोला है कि सभी निचले मामले अक्षर az श्रेणी में हैं ...
AProgrammer

1
"% [^ \ n]" के रूप में एक ही समस्या है (), बफर अतिप्रवाह। अतिरिक्त पकड़ के साथ कि \ n फाइनल नहीं पढ़ा जाता है; यह इस तथ्य से छिपा होगा कि अधिकांश प्रारूप सफेद रिक्त स्थान को छोड़ कर शुरू होते हैं, लेकिन उनमें से एक नहीं है। मैं स्ट्रिंग को पढ़ने के लिए स्कैनफ का उपयोग करने पर उदाहरण नहीं समझता।
एपीग्रामग्राम

1
sइनपुट स्ट्रिंग के अंत से हटा दिया गया है क्योंकि यह कुछ मामलों में बहुत ही कम और गलत है (जैसा कि पहले टिप्पणियों में बताया गया है)। [यह एक के कुछ भिन्नता के बजाय स्वयं प्रारूप निर्दिष्टकर्ता है s
paxdiablo

54

यह उदाहरण एक उल्टे स्कैनसेट का उपयोग करता है, इसलिए जब तक यह एक '\ n' का सामना नहीं करता है, तब तक स्कैनफ मानों को लेता रहता है - इसलिए रिक्त स्थान भी बच जाते हैं

#include <stdio.h>

int main (int argc, char const *argv[])
{
    char name[20];
    scanf("%[^\n]s",name);
    printf("%s\n", name);
    return 0;
}

1
बफर ओवरफ्लो के साथ सावधानी बरते। यदि उपयोगकर्ता 50 अक्षरों के साथ "नाम" लिखता है, तो प्रोग्राम संभवतः क्रैश हो जाएगा।
ब्रूनोइस

3
जैसा कि आप बफर आकार जानते हैं, आप %20[^\n]s
ऑसविन

45 स्कोर और कोई भी sवहाँ होने का स्पष्ट माल खेती की ओर इशारा किया !
अंती हापला

22

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

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

या यह

void getText(char *message, char *variable, int size){
    printf("\n %s: ", message);
    fgets(variable, sizeof(char) * size, stdin);
    sscanf(variable, "%[^\n]", variable);
}

char name[20];
getText("Your name", name, 20);

डेमो


1
मैंने परीक्षण नहीं किया, लेकिन इस पृष्ठ में अन्य उत्तरों के आधार पर, मेरा मानना ​​है कि आपके उदाहरण में स्कैनफ के लिए सही बफर आकार होगा: scanf("%19[^\n]", name);(संक्षिप्त उत्तर के लिए अभी भी +1)
डॉ। बनो

1
एक साइड नोट के रूप में, हमेशाsizeof(char) परिभाषा 1 से होता है, इसलिए इसके द्वारा गुणा करने की कोई आवश्यकता नहीं है।
पैक्सडीब्लो

8

scanf()फ़ील्ड चौड़ाई निर्दिष्ट किए बिना स्ट्रिंग पढ़ने के लिए उपयोग न करें । आपको त्रुटियों के लिए रिटर्न मान भी जांचना चाहिए:

#include <stdio.h>

#define NAME_MAX    80
#define NAME_MAX_S "80"

int main(void)
{
    static char name[NAME_MAX + 1]; // + 1 because of null
    if(scanf("%" NAME_MAX_S "[^\n]", name) != 1)
    {
        fputs("io error or premature end of line\n", stderr);
        return 1;
    }

    printf("Hello %s. Nice to meet you.\n", name);
}

वैकल्पिक रूप से, उपयोग करें fgets():

#include <stdio.h>

#define NAME_MAX 80

int main(void)
{
    static char name[NAME_MAX + 2]; // + 2 because of newline and null
    if(!fgets(name, sizeof(name), stdin))
    {
        fputs("io error\n", stderr);
        return 1;
    }

    // don't print newline
    printf("Hello %.*s. Nice to meet you.\n", strlen(name) - 1, name);
}

6

आप fgets()एक स्ट्रिंग को पढ़ने के लिए फ़ंक्शन का उपयोग कर सकते हैं या उपयोग कर सकते हैं scanf("%[^\n]s",name);ताकि स्ट्रिंग रीडिंग एक नई पंक्ति वर्ण का सामना करने पर समाप्त हो जाए।


सावधान कि यह बफर ओवरफ्लो को रोकता नहीं है
ब्रूनोइस

sवहाँ नहीं है
अंटी हापला

5

getline()

अब POSIX का हिस्सा, कोई नहीं-कम।

यह बफर आवंटन समस्या का भी ध्यान रखता है जो आपने पहले के बारे में पूछा था, हालांकि आपको freeमेमोरी को इंगेज करने का ध्यान रखना होगा ।


स्टैंडर्ड? संदर्भ में आप उद्धृत करते हैं: "दोनों गेटलाइन () और गेटडेलिम () जीएनयू एक्सटेंशन हैं।"
एपीग्रामग्राम

1
POSIX 2008 गेटलाइन जोड़ता है। इसलिए GNU आगे बढ़ा और 2.9 संस्करण के आसपास glibc के लिए अपने हेडर बदले, और यह कई परियोजनाओं के लिए परेशानी पैदा कर रहा है। नहीं एक निश्चित लिंक, लेकिन देखो यहाँ: bugzilla.redhat.com/show_bug.cgi?id=493941 । ऑन-लाइन मैन पेज के लिए, मैंने पहले एक गूगल पाया को पकड़ा।
dmckee --- पूर्व-मध्यस्थ बिल्ली का बच्चा

3

यदि कोई अभी भी देख रहा है, तो यहां मेरे लिए क्या काम किया है - रिक्त स्थान सहित स्ट्रिंग की एक मनमानी लंबाई पढ़ने के लिए।

इस सरल और सुरुचिपूर्ण समाधान को साझा करने के लिए वेब पर कई पोस्टर के लिए धन्यवाद। अगर यह काम करता है तो क्रेडिट उनके पास जाता है लेकिन कोई भी त्रुटि मेरी है।

char *name;
scanf ("%m[^\n]s",&name);
printf ("%s\n",name);

2
यह ध्यान देने योग्य है कि यह एक POSIX एक्सटेंशन है और ISO मानक में मौजूद नहीं है। पूर्णता के लिए, आपको संभवत: errnoआवंटित मेमोरी की भी जाँच और सफाई करनी चाहिए ।
paxdiablo

sस्कैन के बाद वहाँ से संबंधित नहीं है
एंटटी हवाला

1

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

int main()
{
  char string[100], c;
  int i;
  printf("Enter the string: ");
  scanf("%s", string);
  i = strlen(string);      // length of user input till first space
  do
  {
    scanf("%c", &c);
    string[i++] = c;       // reading characters after first space (including it)
  } while (c != '\n');     // until user hits Enter
  string[i - 1] = 0;       // string terminating
return 0;
}

यह कैसे काम करता है? जब उपयोगकर्ता मानक इनपुट से वर्ण इनपुट करते हैं, तो उन्हें पहले रिक्त स्थान तक स्ट्रिंग चर में संग्रहीत किया जाएगा । उसके बाद, शेष प्रविष्टि इनपुट स्ट्रीम में रहेगी, और अगले स्कैनफ़ की प्रतीक्षा करें। अगला, हमारे पास एक forलूप है जो इनपुट स्ट्रीम (तब तक \n) से चार ले जाता है और उन्हें स्ट्रिंग चर के अंत में भेज देता है , इस प्रकार कीबोर्ड से उपयोगकर्ता इनपुट के रूप में एक पूर्ण स्ट्रिंग बनाता है।

आशा है कि यह किसी की मदद करेगा!


बफर ओवरफ्लो के अधीन।
paxdiablo

0

क्या तुम सच में उपयोग नहीं करना चाहिए जबकि scanf()देखते हैं क्योंकि बात की इस तरह के लिए, ज्यादा बेहतर तरह के रूप में कॉल gets()या getline(), यह किया जा सकता है:

#include <stdio.h>

char* scan_line(char* buffer, int buffer_size);

char* scan_line(char* buffer, int buffer_size) {
   char* p = buffer;
   int count = 0;
   do {
       char c;
       scanf("%c", &c); // scan a single character
       // break on end of line, string terminating NUL, or end of file
       if (c == '\r' || c == '\n' || c == 0 || c == EOF) {
           *p = 0;
           break;
       }
       *p++ = c; // add the valid character into the buffer
   } while (count < buffer_size - 1);  // don't overrun the buffer
   // ensure the string is null terminated
   buffer[buffer_size - 1] = 0;
   return buffer;
}

#define MAX_SCAN_LENGTH 1024

int main()
{
   char s[MAX_SCAN_LENGTH];
   printf("Enter a string: ");
   scan_line(s, MAX_SCAN_LENGTH);
   printf("got: \"%s\"\n\n", s);
   return 0;
}

2
वहाँ एक है कारण क्यों getsपदावनत और हटाया (गया था stackoverflow.com/questions/30890696/why-gets-is-deprecated मानक से)। यह और भी बुरा है scanfक्योंकि कम से कम बाद में इसे सुरक्षित बनाने के तरीके हैं।
paxdiablo

-1
/*reading string which contains spaces*/
#include<stdio.h>
int main()
{
   char *c,*p;
   scanf("%[^\n]s",c);
   p=c;                /*since after reading then pointer points to another 
                       location iam using a second pointer to store the base 
                       address*/ 
   printf("%s",p);
   return 0;
 }

4
क्या आप बता सकते हैं कि यह सही उत्तर क्यों है? कृपया कोड-केवल उत्तर पोस्ट न करें।
थियो

sस्कैन के बाद वहां मौजूद नहीं हैं
एंटटी हवाला
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.