2012-11-07 13 views
5

निम्न फ़ंक्शन में, मैं यह देखने का प्रयास करता हूं कि कोई स्ट्रिंग sT टाइप करने के लिए परिवर्तनीय है या नहीं, यह देखकर कि मैं T टाइप कर सकता हूं, और यदि इनपुट पूरी तरह से बाद में खाया जाता है। मैंमैं कैसे जांच सकता हूं कि स्ट्रीम निष्कर्षण ने सभी इनपुट का उपभोग किया है?

template <class T> 
bool can_be_converted_to(const std::string& s, T& t) 
{ 
    std::istringstream i(s); 
    i>>std::boolalpha; 
    i>>t; 
    if (i and i.eof()) 
    return true; 
    else 
    return false; 
} 

हालांकि चाहते हैं, can_be_converted_to<bool>("true"), गलत का आकलन क्योंकि i.eof() समारोह के अंत में गलत है।

यह सही है, भले ही फ़ंक्शन ने पूरी स्ट्रिंग पढ़ ली है, क्योंकि इसने स्ट्रिंग के अंत को पढ़ने का प्रयास नहीं किया है। (तो, जाहिरा तौर पर इस समारोह पूर्णांक और डबल के लिए क्योंकि istringstream अंत अतीत पढ़ता है जब इन पढ़ने काम करता है।)

तो, यह सोचते हैं कि मैं वास्तव में (i and <input completely consumed>) जाँच की जानी चाहिए:

प्रश्न: मैं इनपुट कि जाँच करते हैं eof() का उपयोग कर पूरी तरह से उपभोग किया गया था?

+0

कोई जवाब नहीं, केवल एक नोट: टाइप करें 'टी' के टीएमपी-वेरिएबल का उपयोग करने पर विचार करें क्योंकि आप 'टी' में भी ओवरराइड करेंगे मामला 'e.eof()' गलत है। – Mene

+0

यदि आप 'i.peek()' करते हैं तो आपको 'EOF' वापस मिल जाता है –

उत्तर

8

उपयोग peek() या get() जाँच करने के लिए धारा में आगे क्या है:

return (i >> std::boolalpha >> t && i.peek() == EOF); 

आपका संस्करण पूर्णांकों के लिए या तो काम नहीं करता है,। इस इनपुट पर विचार करें: 123 45। यह 123 पढ़ेगा और सत्य रिपोर्ट करेगा, भले ही स्ट्रीम में कुछ वर्ण शेष हैं।

3

मानक पुस्तकालय के कई कार्यान्वयन में eof केवल अंत से परे पढ़ने की कोशिश करने के बाद सेट किया जाएगा। अपने कोड में पुष्टि कर सकते हैं कि ऐसा करने से:

char _; 
if (i && !(i >> _)) { // i is in a valid state, but 
         // reading a single extra char fails 
3

ज्रोक के जवाब पर विस्तार, आप i.get() बस के रूप में आसानी से i.peek() के रूप में उपयोग कर सकते हैं, कम से कम इस मामले में। इसके अलावा (मैं अगर वहाँ किसी भी कारण से दूसरे के पसंद करते हैं करने के लिए है पता नहीं।)

, परंपरा है कि सफेद स्थान कुछ भी लेकिन एक विभाजक, आप के लिए जाँच से पहले यह निकालने के लिए चाहते हो सकता है कभी नहीं है निम्नलिखित समाप्त। कुछ की तरह:

return i >> std::ws && i.get() == std::istream::traits_type::eof(); 

std::ws के कुछ पुराने कार्यान्वयन गाड़ी थे, और एक त्रुटि राज्य में धारा जाते थे।

return !(i >> std::ws) || i.get() == std::istream::traits_type::eof(); 

या बस शर्त से पहले std::ws पढ़ा है, और i.get() पर विशिष्ट निर्भर करते हैं: उस मामले में, आप परीक्षण व्युत्क्रम, और की तरह कुछ करना होगा।

(यदि गाड़ी std::ws अभी भी एक समस्या है मैं नहीं जानता। मैं इसके बारे में एक संस्करण वापस काम किया जब यह था विकसित की है, और मैं बस को जारी रखा है में उपयोग करें।)

2

मैं करूंगा एक पूरी तरह से अलग दृष्टिकोण की पेशकश करना: अपनी इनपुट स्ट्रिंग लें, इसे स्वयं टोकन करें, और फिर boost::lexical_cast<T> का उपयोग करके अलग-अलग फ़ील्ड को परिवर्तित करें।

कारण: मैंने एक दोपहर बर्बाद कर दिया जिसमें दो int और 2 डबल फ़ील्ड होते हैं, जो रिक्त स्थान से अलग होते हैं।

int i, j; 
double x, y; 
std::istringstream ins{str}; 

ins >> i >> j >> x >> y; 
// how to check errors???... 

`"5 3 9.9e+01 5.5e+02"` 

सही ढंग से के रूप में सही इनपुट ऐसे पार्स करता है, लेकिन इस के साथ समस्या यह पता नहीं लगा पाया:

`"5 9.6e+01 5.5e+02"` 

क्या होता है कि i सेट हो जाएगा निम्न कार्य 5 (ठीक), j 9 (??), x से 6.0 (= 0.6e + 01), y से 550 (ठीक) पर सेट किया जाएगा। मैं failbit सेट नहीं होने के लिए आश्चर्यचकित था ... (मंच जानकारी: ओएस एक्स 10.9, ऐप्पल क्लैंग ++ 6.0, सी ++ 11 मोड)।

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

ओटीओएच, यदि आप "मारियस" उत्कृष्ट tokeniser function का उपयोग करते हैं और पहले व्हाइटस्पेस पर str विभाजित करते हैं तो अचानक सब कुछ बहुत आसान हो जाता है। टोकनिसर का थोड़ा संशोधित संस्करण यहां दिया गया है। मैंने तारों के वेक्टर को वापस करने के लिए इसे फिर से लिखा; मूल एक टेम्पलेट है जो टोकन को एक कंटेनर में तारों में परिवर्तनीय तत्वों के साथ रखता है। (जो लोग इस तरह के एक सामान्य दृष्टिकोण की जरूरत के लिए ऊपर मूल लिंक से परामर्श लें।)

// \param str: the input string to be tokenized 
// \param delimiters: string of delimiter characters 
// \param trimEmpty: if true then empty tokens will be trimmed 
// \return a vector of strings containing the tokens 
std::vector<std::string> tokenizer(
    const std::string& str, 
    const std::string& delimiters = " ", 
    const bool trimEmpty = false 
) { 
    std::vector<std::string> tokens; 
    std::string::size_type pos, lastPos = 0; 
    const char* strdata = str.data(); 
    while(true) { 
     pos = str.find_first_of(delimiters, lastPos); 
     if(pos == std::string::npos) { 
      // no more delimiters 
      pos = str.length(); 
      if(pos != lastPos || !trimEmpty) { 
       tokens.emplace_back(strdata + lastPos, pos - lastPos); 
      } 
      break; 
     } else { 
      if(pos != lastPos || !trimEmpty) { 
       tokens.emplace_back(strdata + lastPos, pos - lastPos); 
      } 
     } 
     lastPos = pos + 1; 
    } 
    return tokens; 
} 

और उसके बाद सिर्फ इस तरह इसका इस्तेमाल (ParseError कुछ अपवाद वस्तु है):

std::vector<std::string> tokens = tokenizer(str, " \t", true); 
if (tokens.size() < 4) 
    throw ParseError{"Too few fields in " + str}; 

try { 
    unsigned int i{ boost::lexical_cast<unsigned int>(tokens[0]) }, 
     j{ boost::lexical_cast<unsigned int>(tokens[1]) }; 
    double x{ boost::lexical_cast<double>(tokens[2]) }, 
     y{ boost::lexical_cast<double>(tokens[3]) }; 
    // print or process i, j, x, y ... 
} catch(const boost::bad_lexical_cast& error) { 
    throw ParseError{"Could not parse " + str}; 
} 

नोट: आप कर सकते हैं यदि आप चाहें तो Boost split या tokenizer का उपयोग करें, लेकिन वे Marius 'टोकनिसर (कम से कम मेरे पर्यावरण में) से धीमे थे।

अद्यतन: boost::lexical_cast<T> के बजाय आप सी ++ 11 "std::sto*" कार्यों का उपयोग कर सकते हैं (उदाहरण के लिए stoi किसी पूर्णांक के लिए एक स्ट्रिंग टोकन कन्वर्ट करने के लिए)। ये दो प्रकार के अपवाद फेंकते हैं: std::invalid_argument यदि रूपांतरण नहीं किया जा सका और std::out_of_range यदि परिवर्तित मूल्य का प्रतिनिधित्व नहीं किया जा सकता है। आप या तो इन अलग-अलग या उनके माता-पिता std::runtime_error को पकड़ सकते हैं। उपर्युक्त उदाहरण कोड में संशोधन पाठक के लिए एक अभ्यास के रूप में छोड़ा गया है :-)

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

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