इस उत्तर में मैं यह मानने जा रहा हूं कि आप पाठ की पंक्तियों को पढ़ रहे हैं और व्याख्या कर रहे हैं । शायद आप उपयोगकर्ता को संकेत दे रहे हैं, जो कुछ टाइप कर रहा है और RETURN को मार रहा है। या शायद आप किसी प्रकार की डेटा फ़ाइल से संरचित पाठ की पंक्तियाँ पढ़ रहे हैं।
चूंकि आप पाठ की पंक्तियों को पढ़ रहे हैं, इसलिए यह आपके कोड को लाइब्रेरी फ़ंक्शन के चारों ओर व्यवस्थित करने के लिए समझ में आता है जो पाठ को पढ़ता है, ठीक है, पाठ की एक पंक्ति। मानक कार्य है fgets()
, हालांकि अन्य (सहित) हैंgetline
) हैं। और फिर अगला कदम पाठ की उस पंक्ति को किसी तरह व्याख्यायित करना है।
fgets
पाठ की एक पंक्ति पढ़ने के लिए कॉल करने का मूल नुस्खा यहां दिया गया है :
char line[512];
printf("type something:\n");
fgets(line, 512, stdin);
printf("you typed: %s", line);
यह केवल पाठ की एक पंक्ति में पढ़ता है और इसे वापस प्रिंट करता है। जैसा कि लिखा गया है कि इसकी कुछ सीमाएँ हैं, जिन्हें हम एक मिनट में प्राप्त कर लेंगे। इसकी एक बहुत बड़ी विशेषता यह भी है: उस नंबर 512 को हम दूसरे तर्क के रूप में पारित fgets
करते हैं, जिस सरणी को line
हम fgets
पढ़ने के लिए कह रहे हैं उसका आकार है
। यह तथ्य - कि हम बता सकते हैं fgets
कि इसे पढ़ने की कितनी अनुमति है - इसका मतलब है कि हम यह सुनिश्चित कर सकते हैं कि fgets
इसमें बहुत अधिक पढ़ने से सरणी को ओवरफ्लो नहीं किया जाएगा।
तो अब हम जानते हैं कि पाठ की एक पंक्ति कैसे पढ़ें, लेकिन क्या होगा यदि हम वास्तव में एक पूर्णांक, या एक फ्लोटिंग-पॉइंट नंबर, या एक एकल वर्ण, या एक शब्द पढ़ना चाहते हैं? (यही कारण है, क्या हुआ अगर
scanf
कॉल हम पर सुधार करने के लिए कोशिश कर रहे हैं एक फॉर्मेट स्पेसिफायर उपयोग करती रही है %d
, %f
, %c
या, %s
?)
इन चीजों में से किसी एक के रूप में - एक स्ट्रिंग - पाठ की एक पंक्ति को फिर से व्याख्या करना आसान है। स्ट्रिंग को पूर्णांक में बदलने के लिए, इसे करने के लिए सबसे सरल (यद्यपि अपूर्ण) तरीका है atoi()
। फ्लोटिंग-पॉइंट नंबर में बदलने के लिए atof()
। (और भी बेहतर तरीके हैं, जैसा कि हम एक मिनट में देखेंगे।) यहाँ एक बहुत ही सरल उदाहरण दिया गया है:
printf("type an integer:\n");
fgets(line, 512, stdin);
int i = atoi(line);
printf("type a floating-point number:\n");
fgets(line, 512, stdin);
float f = atof(line);
printf("you typed %d and %f\n", i, f);
यदि आप चाहते थे कि उपयोगकर्ता किसी एक वर्ण को टाइप करे (शायद y
या
n
हाँ / नहीं प्रतिक्रिया के रूप में), तो आप शाब्दिक रूप से इस तरह से पंक्ति के पहले चरित्र को पकड़ सकते हैं:
printf("type a character:\n");
fgets(line, 512, stdin);
char c = line[0];
printf("you typed %c\n", c);
(यह ध्यान नहीं देता है, निश्चित रूप से, संभावना है कि उपयोगकर्ता ने एक बहु-वर्ण प्रतिक्रिया टाइप की है; यह चुपचाप टाइप किए गए किसी भी अतिरिक्त वर्ण की उपेक्षा करता है।)
अंत में, यदि आप चाहते थे कि उपयोगकर्ता एक स्ट्रिंग टाइप करे जिसमें निश्चित रूप से व्हाट्सएप न हो , यदि आप इनपुट लाइन का इलाज करना चाहते थे
hello world!
जैसा कि स्ट्रिंग के "hello"
बाद कुछ और होता है (जो कि scanf
प्रारूप क्या %s
होता है), ठीक है, उस मामले में, मैंने थोड़ा सा फाइब किया है, इस तरह से लाइन को फिर से व्याख्या करना इतना आसान नहीं है, आखिरकार, इसलिए इसका जवाब है सवाल का हिस्सा थोड़ा इंतजार करना होगा।
लेकिन पहले मैं तीन चीजों पर वापस जाना चाहता हूं जिन्हें मैंने छोड़ दिया।
(१) हम बुला रहे हैं
fgets(line, 512, stdin);
सरणी में पढ़ने के लिए line
, और जहां 512 सरणी का आकार है, line
इसलिए fgets
इसे ओवरफ्लो नहीं करना जानता है। लेकिन यह सुनिश्चित करने के लिए कि 512 सही संख्या है (विशेष रूप से, यह जांचने के लिए कि शायद किसी ने आकार बदलने के लिए कार्यक्रम को बदल दिया है), आपको जहां भी line
घोषित किया गया था वहां वापस पढ़ना होगा । यह एक उपद्रव है, इसलिए आकारों को सिंक में रखने के दो बेहतर तरीके हैं। आप (a) आकार के लिए नाम बनाने के लिए प्रीप्रोसेसर का उपयोग कर सकते हैं:
#define MAXLINE 512
char line[MAXLINE];
fgets(line, MAXLINE, stdin);
या, (बी) सी के sizeof
ऑपरेटर का उपयोग करें :
fgets(line, sizeof(line), stdin);
(२) दूसरी समस्या यह है कि हम त्रुटि की जाँच नहीं कर रहे हैं। जब आप इनपुट पढ़ रहे होते हैं, तो आपको हमेशा त्रुटि की संभावना की जांच करनी चाहिए । यदि किसी भी कारण से fgets
आप पाठ की उस पंक्ति को नहीं पढ़ सकते हैं, जो यह इंगित करता है कि यह अशक्त सूचक लौटाता है। इसलिए हमें जैसी चीजें करनी चाहिए थीं
printf("type something:\n");
if(fgets(line, 512, stdin) == NULL) {
printf("Well, never mind, then.\n");
exit(1);
}
अंत में, यह मुद्दा है कि पाठ की एक पंक्ति को पढ़ने के लिए,
fgets
पात्रों को पढ़ता है और उन्हें आपके सरणी में भरता है जब तक कि यह उस \n
चरित्र को नहीं पाता है जो रेखा को समाप्त करता है, और यह \n
चरित्र को आपके सरणी में भी भर देता है । आप इसे देख सकते हैं यदि आप हमारे पहले के उदाहरण को थोड़ा संशोधित करते हैं:
printf("you typed: \"%s\"\n", line);
अगर मैं इसे चलाता हूं और "स्टीव" टाइप करता हूं तो यह मुझे संकेत देता है, यह प्रिंट करता है
you typed: "Steve
"
यह "
दूसरी पंक्ति पर है क्योंकि यह जो स्ट्रिंग पढ़ता है और वापस प्रिंट करता है वह वास्तव में था "Steve\n"
।
कभी-कभी यह अतिरिक्त न्यूलाइन मायने नहीं रखती है (जैसे कि जब हमने कॉल किया
atoi
या atof
, क्योंकि वे दोनों नंबर के बाद किसी भी अतिरिक्त गैर-संख्यात्मक इनपुट को अनदेखा करते हैं), लेकिन कभी-कभी यह बहुत मायने रखता है। इसलिए अक्सर हम उस न्यूलाइन को अलग करना चाहते हैं। ऐसा करने के कई तरीके हैं, जो मुझे एक मिनट में मिल जाएंगे। (मुझे पता है कि मैं बहुत कुछ कह रहा हूं। लेकिन मैं उन सभी चीजों को वापस पा लूंगा, मैं वादा करता हूं।)
इस बिंदु पर, आप सोच रहे होंगे: "मुझे लगा कि आपने कहा scanf
था कि यह अच्छा नहीं है, और यह अन्य तरीका इतना बेहतर होगा। लेकिन fgets
यह एक उपद्रव जैसा लगने लगा है। कॉल करना इतना आसानscanf
था ? क्या मैं इसका उपयोग नहीं कर सकता?" "
scanf
यदि आप चाहें, तो ज़रूर, आप इसका उपयोग कर सकते हैं। (और वास्तव में
सरल चीजों के लिए, कुछ मायनों में यह सरल है।) लेकिन, कृपया, मेरे पास रोना मत आना जब यह अपने 17 quirks और foibles में से एक के कारण आपको विफल कर देता है, या इनपुट के कारण एक अनंत लूप में चला जाता है उम्मीद नहीं की थी, या जब आप यह पता नहीं लगा सकते हैं कि कुछ और अधिक जटिल करने के लिए इसका उपयोग कैसे करें। और चलो fgets
वास्तविक उपद्रवों पर एक नज़र डालें :
आपको हमेशा सरणी आकार निर्दिष्ट करना होगा। ठीक है, निश्चित रूप से, यह एक उपद्रव नहीं है - यह एक विशेषता है, क्योंकि बफर अतिप्रवाह एक बहुत बुरी बात है।
आपको रिटर्न वैल्यू चेक करनी होगी। वास्तव में, यह एक धोने है, क्योंकि scanf
सही तरीके से उपयोग करने के लिए , आपको इसके रिटर्न मूल्य की भी जांच करनी होगी।
आपको \n
पीछे से पट्टी करना होगा। यह, मैं मानता हूं, एक सच्चा उपद्रव है। काश एक मानक कार्य होता जो मैं आपको बता सकता था कि यह कोई छोटी समस्या नहीं है। (कृपया कोई नहीं लाए gets
।) लेकिन scanf's
17 अलग-अलग उपद्रवों की तुलना में , मैं fgets
किसी भी दिन का यह एक उपद्रव लूंगा ।
तो कैसे करते हैं आपको लगता है कि न्यू लाइन पट्टी? तीन तरीके से:
(ए) स्पष्ट तरीका:
char *p = strchr(line, '\n');
if(p != NULL) *p = '\0';
(बी) मुश्किल और कॉम्पैक्ट तरीका:
strtok(line, "\n");
दुर्भाग्य से यह एक हमेशा काम नहीं करता है।
(ग) एक और कॉम्पैक्ट और हल्का अस्पष्ट तरीका:
line[strcspn(line, "\n")] = '\0';
की खामियों: और अब है कि रास्ते से बाहर है, हम वापस एक और बात मैं आप छोड़ करने के लिए प्राप्त कर सकते हैं atoi()
और atof()
। उन लोगों के साथ समस्या यह है कि वे आपको सफलता या विफलता की सफलता का कोई उपयोगी संकेत नहीं देते हैं: वे चुपचाप गैर-इनपुट इनपुट को अनदेखा करते हैं, और वे चुपचाप वापस लौटते हैं यदि कोई संख्यात्मक इनपुट नहीं है। पसंदीदा विकल्प - जिसके कुछ अन्य फायदे भी हैं - strtol
और हैं strtod
।
strtol
आपको 10 के अलावा एक आधार का उपयोग करने देता है, जिसका अर्थ है कि आप (अन्य चीजों के बीच) %o
या का प्रभाव प्राप्त कर सकते हैं । लेकिन इन कार्यों को सही तरीके से कैसे उपयोग किया जाए, यह दिखाना अपने आप में एक कहानी है, और जो पहले से ही एक बहुत ही खंडित कथा में बदल रहा है, उससे बहुत अधिक व्याकुलता होगी, इसलिए मैं अब उनके बारे में अधिक कुछ नहीं कहने जा रहा हूं।%x
साथscanf
मुख्य कथा चिंताओं के बाकी इनपुट आप पार्स करने की कोशिश कर रहे होंगे जो कि केवल एक संख्या या वर्ण से अधिक जटिल है। क्या होगा यदि आप दो संख्याओं वाली एक पंक्ति, या एकाधिक व्हाट्सएप-पृथक शब्द, या विशिष्ट फ़्रेमिंग विराम चिह्न पढ़ना चाहते हैं? scanf
यहीं चीजें दिलचस्प हो जाती हैं, और जहां चीजें संभवतः जटिल हो रही थीं यदि आप चीजों का उपयोग करने की कोशिश कर रहे थे , और जहां अब बहुत अधिक विकल्प हैं, तो आपने साफ-साफ पाठ की एक पंक्ति का उपयोग करके पढ़ा है fgets
, हालांकि उन सभी विकल्पों पर पूरी कहानी शायद एक पुस्तक भर सकता है, इसलिए हम केवल यहाँ सतह को खरोंचने में सक्षम होने जा रहे हैं।
मेरी पसंदीदा तकनीक व्हॉट्सएप-अलग-अलग "शब्दों" में लाइन को तोड़ना है, फिर प्रत्येक "शब्द" के साथ आगे कुछ करें। ऐसा करने के लिए एक प्रमुख मानक कार्य है
strtok
(जिसमें इसके मुद्दे भी हैं, और जो एक अलग चर्चा भी करता है)। मेरी अपनी प्राथमिकता प्रत्येक टूटे-फूटे "शब्द" के लिए एक व्यूह-रचना बनाने के लिए एक समर्पित फ़ंक्शन है, एक फ़ंक्शन जो मैं इन कोर्स नोट्स में वर्णित करता हूं
। किसी भी दर पर, एक बार जब आप "शब्द" प्राप्त कर लेते हैं, तो आप हर एक को आगे की प्रक्रिया कर सकते हैं, शायद उसी atoi
/ atof
/ strtol
/ strtod
फ़ंक्शन के साथ जो हमने पहले ही देखा है।
विरोधाभासी रूप से, भले ही हम समय और प्रयास की एक उचित राशि खर्च कर रहे हैं, लेकिन यह पता लगाने के लिए कि किस scanf
तरह से दूर जाना है , पाठ की लाइन से निपटने के लिए एक और बढ़िया तरीका है जिसे हम fgets
इसे पढ़ते हैं
sscanf
। इस तरह, आप अधिकांश लाभों के साथ समाप्त होते हैं scanf
, लेकिन अधिकांश नुकसान के बिना।
यदि आपका इनपुट सिंटैक्स विशेष रूप से जटिल है, तो इसे पार्स करने के लिए "रेगेक्सपी" लाइब्रेरी का उपयोग करना उचित हो सकता है।
अंत में, आप जो भी तदर्थ पार्सिंग समाधान आप पर सूट करते हैं, उसका उपयोग कर सकते हैं । आप अपने द्वारा char *
अपेक्षित वर्णों के लिए पॉइंटर जाँच के साथ एक समय में एक लाइन के माध्यम से चरित्र को स्थानांतरित
कर सकते हैं। या आप कार्यों का उपयोग कर विशिष्ट वर्ण के लिए खोज कर सकते हैं strchr
या strrchr
, या strspn
या strcspn
, या strpbrk
। या फिर आप को पार्स / परिवर्तित और का उपयोग कर अंकों पात्रों के समूहों पर छोड़ सकते हैं strtol
या
strtod
कार्यों कि हम पहले से अधिक छोड़ दिया।
स्पष्ट रूप से बहुत कुछ है जो कहा जा सकता है, लेकिन उम्मीद है कि यह परिचय आपको मिल जाएगा।
(r = sscanf("1 2 junk", "%d%d", &x, &y)) != 2
अनुगामी गैर-संख्यात्मक पाठ के रूप में खराब का पता नहीं चलता है।