क्यों होता है ऐसा?
यह आपके द्वारा प्रदान किए गए इनपुट के साथ बहुत कम है, लेकिन डिफ़ॉल्ट व्यवहार के साथ std::getline()
प्रदर्शित करता है। जब आपने नाम के लिए अपना इनपुट प्रदान किया ( std::cin >> name
), तो आपने न केवल निम्नलिखित वर्णों को जमा किया, बल्कि एक निहित न्यूलाइन को धारा में जोड़ा गया:
"John\n"
जब आप चुनते हैं Enterया Returnटर्मिनल से सबमिट करते समय आपके इनपुट पर एक नई पंक्ति हमेशा जोड़ दी जाती है । इसका उपयोग अगली पंक्ति की ओर बढ़ने के लिए फाइलों में भी किया जाता है। न्यूलाइन को name
अगले I / O ऑपरेशन तक निष्कर्षण के बाद बफर में छोड़ दिया जाता है जहां इसे या तो छोड़ दिया जाता है या उपभोग किया जाता है। जब नियंत्रण का प्रवाह पहुंचता है std::getline()
, तो नई रेखा को छोड़ दिया जाएगा, लेकिन इनपुट तुरंत बंद हो जाएगा। ऐसा होने का कारण यह है कि इस फ़ंक्शन की डिफ़ॉल्ट कार्यक्षमता यह निर्धारित करती है कि यह (यह एक पंक्ति को पढ़ने का प्रयास करता है और जब यह एक नई रूपरेखा पाता है तो रुक जाता है)।
क्योंकि यह अग्रणी न्यूलाइन आपके प्रोग्राम की अपेक्षित कार्यक्षमता को बाधित करता है, इसलिए यह इस प्रकार है कि इसे किसी भी तरह से हमारे अनदेखे को छोड़ देना चाहिए। एक विकल्प std::cin.ignore()
पहले निष्कर्षण के बाद कॉल करना है । यह अगले उपलब्ध चरित्र को छोड़ देगा ताकि न्यूलाइन अब रास्ते में न रहे।
std::getline(std::cin.ignore(), state)
इन-डेप्थ स्पष्टीकरण:
यह std::getline()
आपके द्वारा कहा जाने वाला अधिभार है :
template<class charT>
std::basic_istream<charT>& getline( std::basic_istream<charT>& input,
std::basic_string<charT>& str )
इस फ़ंक्शन का एक और अधिभार प्रकार का एक सीमांकक लेता है charT
। एक सीमांकक चरित्र एक ऐसा चरित्र है जो इनपुट के दृश्यों के बीच की सीमा का प्रतिनिधित्व करता है। यह विशेष रूप से अधिभार, input.widen('\n')
डिफ़ॉल्ट रूप से न्यूलाइन वर्ण में सीमांकक सेट करता है क्योंकि एक की आपूर्ति नहीं की गई थी।
अब, ये कुछ स्थितियां हैं, जिससे std::getline()
इनपुट समाप्त होता है:
- यदि धारा ने वर्णों की अधिकतम राशि निकाली है, जो
std::basic_string<charT>
धारण कर सकती है
- यदि अंत-फ़ाइल (ईओएफ) चरित्र पाया गया है
- अगर सीमांकक पाया गया है
तीसरी स्थिति वह है जिससे हम निपट रहे हैं। इस प्रकार आपके इनपुट state
का प्रतिनिधित्व किया जाता है:
"John\nNew Hampshire"
^
|
next_pointer
जहां next_pointer
अगले चरित्र को पार्स किया जाना है। चूंकि इनपुट अनुक्रम में अगली स्थिति में संग्रहीत चरित्र सीमांकक है, इसलिए std::getline()
चुपचाप उस चरित्र को छोड़ देंगे, next_pointer
अगले उपलब्ध चरित्र में वृद्धि , और इनपुट रोक देंगे । इसका मतलब यह है कि आपके द्वारा प्रदान किए गए बाकी अक्षर अभी भी अगले I / O ऑपरेशन के लिए बफर में बने हुए हैं। आप देखेंगे कि यदि आप लाइन में से एक और रीड करते हैं state
, तो आपका निष्कर्षण सही परिणाम देगा क्योंकि अंतिम कॉल को std::getline()
सीमांकित को खारिज कर दिया जाएगा।
आपने देखा होगा कि स्वरूपित इनपुट ऑपरेटर ( operator>>()
) के साथ निकालने पर आप आमतौर पर इस समस्या में नहीं आते हैं । ऐसा इसलिए है क्योंकि इनपुट स्ट्रीम इनपुट के लिए व्हॉट्सएप को सीमांकक के रूप में उपयोग करते हैं और डिफ़ॉल्ट रूप से std::skipws
1 मैनिपुलेटर सेट करते हैं। जब धारा स्वरूपित इनपुट करने के लिए धारा धारा से प्रमुख व्हाट्सएप को छोड़ देगी। 2
स्वरूपित इनपुट ऑपरेटरों के विपरीत, std::getline()
एक है अस्वरूपित इनपुट समारोह। और सभी बिना सूचना वाले इनपुट फ़ंक्शंस में निम्नलिखित कोड कुछ हद तक आम हैं:
typename std::basic_istream<charT>::sentry ok(istream_object, true);
उपरोक्त एक संतरी वस्तु है, जो सभी मानक C ++ क्रियान्वयन में प्रारूपित / गैर-स्वरूपित I / O फ़ंक्शन में त्वरित है। संतरी वस्तुओं का उपयोग I / O के लिए स्ट्रीम तैयार करने और यह निर्धारित करने के लिए किया जाता है कि यह विफल स्थिति में है या नहीं। आपको केवल यह पता चलेगा कि अनियंत्रित इनपुट फ़ंक्शंस में, संतरी कंस्ट्रक्टर का दूसरा तर्क है true
। उस तर्क का मतलब है कि इनपुट अनुक्रम की शुरुआत से अग्रणी व्हाट्सएप को नहीं छोड़ा जाएगा। यहाँ मानक से प्रासंगिक उद्धरण है [the27.7.2.1.3 / 2]:
explicit sentry(basic_istream<charT, traits>& is, bool noskipws = false);
[...] यदि noskipws
शून्य है और is.flags() & ios_base::skipws
नॉनजेरो है, तो फंक्शन अर्क निकालता है और प्रत्येक वर्ण को तब तक हटाता है जब तक कि अगला उपलब्ध इनपुट चरित्र c
एक व्हाट्सएप चरित्र है। [...]
चूंकि उपरोक्त स्थिति झूठी है, संतरी वस्तु व्हॉट्सएप को नहीं छोड़ेगी। इस फ़ंक्शन द्वारा इसका कारण noskipws
निर्धारित किया true
गया है क्योंकि इसका std::getline()
उद्देश्य कच्चे, बिना वर्ण वाले वर्णों को एक std::basic_string<charT>
वस्तु में पढ़ना है ।
समाधान:
के इस व्यवहार को रोकने का कोई तरीका नहीं है std::getline()
। आपको जो करना है वह नई लाइन को std::getline()
चलाने से पहले अपने आप को छोड़ना होगा (लेकिन इसे फ़ॉर्मेट किए गए निष्कर्षण के बाद करें)। यह तब तक किया जा सकता है ignore()
, जब तक कि हम एक नई नई लाइन तक पहुँचने के लिए शेष इनपुट का उपयोग न कर लें:
if (std::cin >> name &&
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') &&
std::getline(std::cin, state))
{ ... }
आपको <limits>
उपयोग करने के लिए शामिल करना होगा std::numeric_limits
। std::basic_istream<...>::ignore()
एक ऐसा फ़ंक्शन है जो वर्णों की एक निर्दिष्ट राशि को तब तक हटाता है जब तक कि वह या तो एक सीमांकक नहीं ढूंढता है या स्ट्रीम के अंत तक पहुंच जाता है ( ignore()
यदि यह मिल जाए तो सीमांकक को भी हटा देता है)। max()
समारोह अक्षर हैं जो एक धारा स्वीकार कर सकते हैं की सबसे बड़ी राशि देता है।
व्हाट्सएप को त्यागने का एक और तरीका यह है कि उस std::ws
फ़ंक्शन का उपयोग किया जाए जो इनपुट स्ट्रीम की शुरुआत से प्रमुख व्हाट्सएप को निकालने और छोड़ने के लिए डिज़ाइन किया गया है:
if (std::cin >> name && std::getline(std::cin >> std::ws, state))
{ ... }
क्या फर्क पड़ता है?
अंतर यह है कि ignore(std::streamsize count = 1, int_type delim = Traits::eof())
3 अंधाधुंध वर्णों का तब तक निर्वहन करता है जब तक कि वह वर्णों को नहीं छोड़ता count
, परिसीमनकर्ता (दूसरे तर्क द्वारा निर्दिष्ट delim
) को पाता है या धारा के अंत को हिट करता है। std::ws
का उपयोग केवल धारा की शुरुआत से व्हाट्सएप वर्णों को छोड़ने के लिए किया जाता है।
यदि आप स्वरूपित इनपुट को बिना इनपुट के साथ मिला रहे हैं और आपको अवशिष्ट व्हाट्सएप का उपयोग करने की आवश्यकता है std::ws
। अन्यथा, यदि आपको अवैध इनपुट को हटाने की आवश्यकता है, भले ही वह क्या है, उपयोग करें ignore()
। हमारे उदाहरण में, हमें केवल व्हाट्सएप को खाली करने की आवश्यकता है क्योंकि स्ट्रीम चर के "John"
लिए आपके इनपुट का उपभोग name
करती है। जो कुछ बचा था वह न्यूलाइन कैरेक्टर था।
1: std::skipws
जोड़तोड़ है जो इनपुट स्ट्रीम को प्रमुख व्हाट्सएप को छोड़ने के लिए कहता है जब स्वरूपित इनपुट प्रदर्शन करते हैं। इसे std::noskipws
मैनिपुलेटर से बंद किया जा सकता है।
2: इनपुट कुछ वर्णों को डिफ़ॉल्ट रूप से व्हाट्सएप के रूप में परिभाषित करता है, जैसे कि स्पेस कैरेक्टर, न्यूलाइन कैरेक्टर, फॉर्म फीड, कैरेट रिटर्न इत्यादि।
3: इस पर हस्ताक्षर हैं std::basic_istream<...>::ignore()
। आप इसे स्ट्रीम से किसी एक वर्ण को छोड़ने के लिए शून्य तर्क के साथ कह सकते हैं, एक तर्क कुछ वर्णों को छोड़ने के लिए, या दो तर्क को count
वर्णों को छोड़ने के लिए या जब तक यह पहुँचता है delim
, जो भी पहले आता है। आप सामान्य std::numeric_limits<std::streamsize>::max()
रूप से मान के रूप में उपयोग count
करते हैं यदि आप नहीं जानते कि परिसीमन से पहले कितने वर्ण हैं, लेकिन आप उन्हें वैसे भी त्यागना चाहते हैं।
std::cin >> name && std::cin >> std::skipws && std::getline(std::cin, state)
कि उम्मीद के मुताबिक काम भी करना चाहिए। (नीचे दिए गए उत्तरों के अतिरिक्त)।