2010-12-28 14 views
13

ayrıştırmak için "sscanf" yerine stringstream kullanıyorum stringstream tarafından sağlanan olanakları kullanmak için sabit biçimli değerleri sscanf türüne güvenli bir alternatif olarak ayıklamak için kullanın. Bunu nasıl yapabilirim? Aşağıdaki özel kullanım durumunu inceleyin. Aşağıdaki sabit biçimde bir std::string var:Sabit biçimdeki bir dizeyi

YYYYMMDDHHMMSSmmm

Nerede:

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) 

Daha önce ben bu satırlar boyunca bir şey yapıyordu:

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); 

genişlik değerleri sihirli sayılar ve sorun değil. Bu değerleri ayıklamak ve tür güvenliği için unsigned s'ye dönüştürmek için akışları kullanmak istiyorum. Ben çalıştığınızda Ama bu:

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

year değerini 0 korur. 2010 olmalıdır.

Yapmaya çalıştığım şeyi nasıl yaparım? Boost'u veya başka bir 3. parti kitaplığını kullanamam, C++ 0x kullanamaz.

+0

* Bill 1 –

+0

'setw ... "Standart C++ IOstreams ve Yerel Ayarlar" onun kopyası için sabırsızlanıyorum() 'çıktı yazmak içindir. Okumak için işe yaramıyor. – marcog

+1

Belki de ilk alanı çıkarmadan önce akışının başına dönmelisiniz. –

cevap

6

biri değil özellikle verimli seçenek bazı geçici dizeleri inşa etmek ve bir sözcük döküm kullanmak olacaktır:

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

lexical_cast kod sadece birkaç satır içinde yerine getirilebilir; Herb Sutter makalesinde, "The String Formatters of Manor Farm." makalesinde yer alan çıplak asgari değeri sunmuştur. Tam olarak aradığınız şey tam olarak değil, sabit genişlikli alanları bir dizeden ayıklamak için güvenli bir yöntemdir.

+0

Boost'un bir parçası olduğu için 'lexical_cast' kullanamıyorum. –

+0

Yine de akarsuları veya bazı "atoi" türlerini kullanabiliyordum. Bunu daha doğal bir şekilde gerçekleştirebileceğimi umuyordum. –

+0

@John: Kendi kolayca yazabilirsiniz. Herb Sutter'ın çok temel bir uygulamanın sunulduğu makalelerinden birine (yedi güzel biçimlendirilmiş kod satırı) bağlandım. Ya da, [ilk Stack Overflow gönderiminde] çok basit bir sürüm yayınladım (http://stackoverflow.com/questions/1528374/how-can-i-extend-a-lexical-cast-to-support-enumerated-types); Biri iki satır koddur. –

4

ben aşağıdakileri kullanın, sizin için yararlı olabilir:itibaren

+0

'stringTo' işlevinde, ekstraksiyondan sonra' iss 'durumunu kontrol etmek ve hataları uygun şekilde işlemek (bir istisna atmak, bir hata kodu döndürmek, uygulamayı iptal etmek, her ne olursa olsun) kontrol etmek çok önemlidir. –

+0

+ 1, temelde, @James'in yukarıda öne sürdüğü şeydir. StdLib tarafından halihazırda verilen bir şeyi kullanmayı umuyordum, ama kendim yazmam gerekebilir –

1

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

YMMV

#include <sstream> 

Kullanımı:

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(); 
    } 

Bu şablonlar gerektirir, bu konuda yararlı olabilecek:

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() sadece dizeleri cstrings arasında okuma için de geçerlidir. Yukarıdaki işlev bu gerçeği kullanır, bir dizgeyi okur ve sonra da istenen türe atar. Herhangi bir türde sabit genişlikli bir alanda okumak için setw() veya ss.width(w) ile birlikte kullanabilirsiniz.

+0

+1 Bu, yine de, @James'in de önerdiği gibi. Burada bir eğilim hissediyorum ... :) –

4

Erm, eğer bu sabit bir formatsa, neden bunu yapmıyorsunuz?

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 jove tarafından, bu sadece işe yarayabilir! –

+0

Temel olarak, >> işlecinin boşluk içerdiği için ayrıştırabileceği yeni bir alan sınırlaması oluşturuyorsunuz ... Çok verimli değil. – 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 çözümü gerçekten güzel, ama beyaz boşluk dizeleri sabit boyutlu ayrıştırma için çalışmaz. Aşağıdaki çözüm düzeltmeleri bu:

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); 
} 

Bu şekilde kullanılabilir:

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