2010-12-28 16 views
13

को पार्स करने के लिए 'sscanf` के बजाय स्ट्रिंगस्ट्रीम का उपयोग करके द्वारा sscanf पर एक प्रकार-सुरक्षित विकल्प के रूप में एक निश्चित प्रारूप string से मूल्य निकालने के लिए प्रदान की गई सुविधाओं का उपयोग करना चाहूंगा। मैं यह कैसे कर सकता हूँ?फिक्स्ड-फॉर्मेट स्ट्रिंग

निम्नलिखित विशिष्ट उपयोग केस पर विचार करें। मैं निम्नलिखित निश्चित प्रारूप में एक std::string है:

YYYYMMDDHHMMSSmmm

कहाँ:

YYYY = 4 digits representing the year 
MM = 2 digits representing the month ('0' padded to 2 characters) 
DD = 2 digits representing the day ('0' padded to 2 characters) 
HH = 2 digits representing the hour ('0' padded to 2 characters) 
MM = 2 digits representing the minute ('0' padded to 2 characters) 
SS = 2 digits representing the second ('0' padded to 2 characters) 
mmm = 3 digits representing the milliseconds ('0' padded to 3 characters) 

पहले मैं इन पंक्तियों के साथ कुछ कर रहा था:

string s = "20101220110651184"; 
unsigned year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, milli = 0;  
sscanf(s.c_str(), "%4u%2u%2u%2u%2u%2u%3u", &year, &month, &day, &hour, &minute, &second, &milli); 

चौड़ाई मूल्यों जादू नंबर दिए गए हैं , और यह ठीक है। मैं इन मानों को निकालने के लिए धाराओं का उपयोग करना चाहता हूं और टाइप सुरक्षा के हित में उन्हें unsigned में परिवर्तित करना चाहता हूं। लेकिन जब मैं कोशिश यह:

stringstream ss; 
ss << "20101220110651184"; 
ss >> setw(4) >> year; 

year मूल्य 0 बरकरार रखती है। यह 2010 होना चाहिए।

मैं जो कर रहा हूं वह मैं कैसे कर सकता हूं? मैं बूस्ट या किसी अन्य तृतीय पक्ष लाइब्रेरी का उपयोग नहीं कर सकता, न ही मैं सी ++ 0x का उपयोग कर सकता हूं।

+0

* विधेयक "मानक सी ++ IOstreams और स्थानों" अपने प्रति के लिए इंतजार नहीं कर सकता ... +1 –

+0

'setw() 'लेखन के लिए है। यह पढ़ने के लिए काम नहीं करता है। – marcog

+1

शायद आपको पहले फ़ील्ड को निकालने से पहले स्ट्रीम की शुरुआत में वापस जाना चाहिए। –

उत्तर

6

एक विशेष रूप से कुशल नहीं विकल्प कुछ अस्थायी श्रृंखला बनाते हैं और एक शाब्दिक डाली उपयोग करने के लिए होगा:

std::string s("20101220110651184"); 
int year = lexical_cast<int>(s.substr(0, 4)); 
// etc. 

lexical_cast सिर्फ कोड की कुछ लाइनों में लागू किया जा सकता है; हर्ब सटर ने अपने लेख में न्यूनतम न्यूनतम प्रस्तुत किया, "The String Formatters of Manor Farm."

यह वही नहीं है जो आप खोज रहे हैं, लेकिन यह स्ट्रिंग से निश्चित-चौड़ाई वाले फ़ील्ड निकालने का एक प्रकार-सुरक्षित तरीका है।

+0

मैं बूस्ट के हिस्से के रूप में 'lexical_cast' का उपयोग नहीं कर सकता। –

+0

हालांकि मैं फिर से स्ट्रीम या कुछ 'atoi' प्रकार की सामग्री का उपयोग कर सकता था। मैं उम्मीद कर रहा था कि मैं इसे एक और प्राकृतिक तरीके से पूरा कर सकता हूं, हालांकि। –

+0

@ जॉन: आप अपना खुद का आसानी से लिख सकते हैं। मैंने हर्ब सटर के लेखों में से एक से जुड़ा हुआ है जहां एक बहुत ही बुनियादी कार्यान्वयन प्रस्तुत किया गया है (कोड की सात अच्छी रूप से स्वरूपित रेखाएं)। या, मैंने [मेरी पहली स्टैक ओवरफ़्लो पोस्ट] में एक बहुत ही सरल संस्करण पोस्ट किया है (http://stackoverflow.com/questions/1528374/how-can-i-extend-a-lexical-cast-to-support-enumerated-types); वह कोड की दो पंक्तियां हैं। –

4

मैं निम्नलिखित का उपयोग करें, यह आप के लिए उपयोगी हो सकता है:

template<typename T> T stringTo(const std::string& s) 
    { 
     std::istringstream iss(s); 
     T x; 
     iss >> x; 
     return x; 
    }; 

template<typename T> inline std::string toString(const T& x) 
    { 
     std::ostringstream o; 
     o << x; 
     return o.str(); 
    } 

इन खाकों की आवश्यकता होती है:

#include <sstream> 

प्रयोग

long date; 
date = stringTo<long>(std::cin); 

YMMV

+0

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

+0

+1 यह मूल रूप से है, मूल रूप से @ जेम्स ऊपर बताता है। मैं पहले से ही StdLib द्वारा प्रदान की गई कुछ चीज़ों का उपयोग करने की उम्मीद कर रहा था, लेकिन मुझे इसे खुद लिखना पड़ सकता है –

1

से, तो आप इस उपयोगी लग सकते:

template<typename T, typename charT, typename traits> 
std::basic_istream<charT, traits>& 
    fixedread(std::basic_istream<charT, traits>& in, T& x) 
{ 
    if (in.width() == 0) 
    // Not fixed size, so read normally. 
    in >> x; 
    else { 
    std::string field; 
    in >> field; 
    std::basic_istringstream<charT, traits> stream(field); 
    if (! (stream >> x)) 
     in.setstate(std::ios_base::failbit); 
    } 
    return in; 
} 

setw() केवल तार cstrings की में पढ़ने के लिए लागू होता है। उपरोक्त फ़ंक्शन इस तथ्य का उपयोग करते हैं, एक स्ट्रिंग में पढ़ते हैं और फिर इसे आवश्यक प्रकार पर कास्टिंग करते हैं। आप इसे किसी भी प्रकार के निश्चित-चौड़ाई वाले फ़ील्ड में पढ़ने के लिए setw() या ss.width(w) के साथ संयोजन में उपयोग कर सकते हैं।

+0

+1 यह, अनिवार्य रूप से, @ जेम्स ने भी सुझाव दिया है। मैं यहां एक प्रवृत्ति महसूस कर रहा हूं ... :) –

4

एर्म, यदि यह निश्चित प्रारूप है, तो आप ऐसा क्यों नहीं करते?

std::string sd("20101220110651184"); 
    // insert spaces from the back 
    sd.insert(14, 1, ' '); 
    sd.insert(12, 1, ' '); 
    sd.insert(10, 1, ' '); 
    sd.insert(8, 1, ' '); 
    sd.insert(6, 1, ' '); 
    sd.insert(4, 1, ' '); 
    int year, month, day, hour, min, sec, ms; 
    std::istringstream str(sd); 
    str >> year >> month >> day >> hour >> min >> sec >> ms; 
+0

जोव द्वारा +1, यह बस काम कर सकता है! –

+0

आप मूल रूप से एक नई स्पेस-सीमांकित स्ट्रिंग बना रहे हैं जो >> ऑपरेटर पार्स कर सकता है क्योंकि इसमें रिक्त स्थान हैं ... बहुत कुशल नहीं है। – BHS

0
template<typename T> 
struct FixedRead { 
    T& content; 
    int size; 
    FixedRead(T& content, int size) : 
      content(content), size(size) { 
     assert(size != 0); 
    } 
    template<typename charT, typename traits> 
    friend std::basic_istream<charT, traits>& 
    operator >>(std::basic_istream<charT, traits>& in, FixedRead<T> x) { 
     int orig_w = in.width(); 
     std::basic_string<charT, traits> o; 
     in >> setw(x.size) >> o; 
     std::basic_stringstream<charT, traits> os(o); 
     if (!(os >> x.content)) 
      in.setstate(std::ios_base::failbit); 
     in.width(orig_w); 
     return in; 
    } 
}; 

template<typename T> 
FixedRead<T> fixed_read(T& content, int size) { 
    return FixedRead<T>(content, size); 
} 

void test4() { 
    stringstream ss("20101220110651184"); 
    int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0, ms = 0; 
    ss >> fixed_read(year, 4) >> fixed_read(month, 2) >> fixed_read(day, 2) 
      >> fixed_read(hour, 2) >> fixed_read(min, 2) >> fixed_read(sec, 2) 
      >> fixed_read(ms, 4); 
    cout << "year:" << year << "," << "month:" << month << "," << "day:" << day 
      << "," << "hour:" << hour << "," << "min:" << min << "," << "sec:" 
      << sec << "," << "ms:" << ms << endl; 
} 
0

ps5mh का समाधान वास्तव में अच्छा है, लेकिन तार कि सफेद रिक्त स्थान शामिल की निश्चित-आकार पार्सिंग के लिए काम नहीं करता। निम्नलिखित समाधान फिक्स यह:

template<typename T, typename T2> 
struct FixedRead 
{ 
    T& content; 
    T2& number; 
    int size; 
    FixedRead(T& content, int size, T2 & number) : 
     content(content), number(number), size(size) 
    { 
     assert (size != 0); 
    } 
    template<typename charT, typename traits> 
    friend std::basic_istream<charT, traits>& 
    operator >>(std::basic_istream<charT, traits>& in, FixedRead<T,T2> x) 
    { 
     if (!in.eof() && in.good()) 
     { 
      std::vector<char> buffer(x.size+1); 
      in.read(buffer.data(), x.size); 
      int num_read = in.gcount(); 
      buffer[num_read] = 0; // set null-termination of string 
      std::basic_stringstream<charT, traits> os(buffer.data()); 
      if (!(os >> x.content)) 
       in.setstate(std::ios_base::failbit); 
      else 
       ++x.number; 
     } 
     return in; 
    } 
}; 
template<typename T, typename T2> 
FixedRead<T,T2> fixedread(T& content, int size, T2 & number) { 
    return FixedRead<T,T2>(content, size, number); 
} 

इस रूप में इस्तेमाल किया जा सकता है:

std::string s = "90007127  19000715790007397"; 
std::vector<int> ints(5); 
int num_read = 0; 
std::istringstream in(s); 
in >> fixedread(ints[0], 8, num_read) 
    >> fixedread(ints[1], 8, num_read) 
    >> fixedread(ints[2], 8, num_read) 
    >> fixedread(ints[3], 8, num_read) 
    >> fixedread(ints[4], 8, num_read); 
// output: 
// num_read = 4 (like return value of sscanf) 
// ints = 90007127, 1, 90007157, 90007397 
// ints[4] is uninitialized 
संबंधित मुद्दे