2012-12-29 15 views
2

我想用'0'作爲填充字符在2個字段上打印一串整數。我可以做到,但會導致代碼重複。我應該如何更改代碼,以便將代碼複製分解出來?如何用相同的格式打印一堆整數?

#include <ctime> 
#include <sstream> 
#include <iomanip> 
#include <iostream> 

using namespace std; 

string timestamp() { 

    time_t now = time(0); 

    tm t = *localtime(&now); 

    ostringstream ss; 

    t.tm_mday = 9; // cheat a little to test it 
    t.tm_hour = 8; 

    ss << (t.tm_year+1900) 
     << setw(2) << setfill('0') << (t.tm_mon+1) // Code duplication 
     << setw(2) << setfill('0') << t.tm_mday 
     << setw(2) << setfill('0') << t.tm_hour 
     << setw(2) << setfill('0') << t.tm_min 
     << setw(2) << setfill('0') << t.tm_sec; 

    return ss.str(); 
} 

int main() { 

    cout << timestamp() << endl; 

    return 0; 
} 

我已經試過

std::ostream& operator<<(std::ostream& s, int i) { 

    return s << std::setw(2) << std::setfill('0') << i; 
} 

,但它沒有工作,operator<<調用是ambigous。


編輯我有4個真棒答案,我選了一個也許是最簡單和最通用的一個(也就是不認爲我們正在處理時間戳)。對於實際問題,我可能會使用std::put_timestrftime

+0

創建一個新的'int'的ostream&operator <<'是一個非常糟糕的主意。 :) –

+0

@LightnessRacesinOrbit是的,我也明白這一點。任何建議,而不是做什麼? – Ali

+1

如果你想格式化日期/時間,你可能想檢查一下:http://en.cppreference.com/w/cpp/io/manip/put_time – zahir

回答

3

您需要爲您的字符串流的代理是這樣的:

struct stream{ 
    std::ostringstream ss; 
    stream& operator<<(int i){ 
     ss << std::setw(2) << std::setfill('0') << i; 
     return *this; // See Note below 
    } 
} ss; 

然後你格式化代碼只會是這樣的:

ss << (t.tm_year+1900) 
    << (t.tm_mon+1) 
    << t.tm_mday 
    << t.tm_hour 
    << t.tm_min 
    << t.tm_sec; 

return ss.ss.str(); 

PS。請注意我的stream :: operator < <()的一般格式,它首先完成它的工作,然後返回一些內容。

+1

Upvoted,謝謝!我不太確定我是否理解你的筆記。是的,你必須返回一個流類型的對象,以便可以調用下一個「operator <<」。我的嘗試還返回了一些東西,即'std :: ostream'。 – Ali

+0

@Ali:也就是's' :) –

0

operator<<(std::ostream& s, int i)是「模棱兩可的」,因爲這樣的功能已經存在。

所有你需要做的就是給那個函數一個不衝突的簽名。

+0

你能給一個工作的例子,最好是一個運算符函數嗎? – Ali

+0

你打算怎麼做,德魯? –

3

「顯而易見的」解決方案是使用操縱器來安裝自定義std::num_put<char>方面,只是根據需要格式化int s。

上述說法雖然完全描述瞭解決方案,但可能有點神祕。以下是實際執行邏輯的代碼。第一個成分是一個特殊的std::num_put<char>方面這只是從std::num_put<char>派生的類,並且覆蓋了它的一個virtual函數。使用的方面是一個過濾方面,用於查看存儲在流中的標記(使用iword())以確定是否應更改行爲。下面是代碼:

class num_put 
    : public std::num_put<char> 
{ 
    std::locale loc_; 
    static int index() { 
     static int rc(std::ios_base::xalloc()); 
     return rc; 
    } 
    friend std::ostream& twodigits(std::ostream&); 
    friend std::ostream& notwodigits(std::ostream&); 

public: 
    num_put(std::locale loc): loc_(loc) {} 
    iter_type do_put(iter_type to, std::ios_base& fmt, 
        char fill, long value) const { 
     if (fmt.iword(index())) { 
      fmt.width(2); 
      return std::use_facet<std::num_put<char> >(this->loc_) 
       .put(to, fmt, '0', value); 
     } 
     else { 
      return std::use_facet<std::num_put<char> >(this->loc_) 
       .put(to, fmt, fill, value); 
     } 
    } 
}; 

的主要部分是do_put()成員函數,其決定的值需要如何被格式化:如果在fmt.iword(index())標誌是非零,它設置寬度2並調用格式函數的填充字符爲0。無論如何寬度將被重置,並且填充字符不會與流存儲在一起,即,不需要任何清理。

通常情況下,代碼可能會生活在一個單獨的翻譯單元中,並且它不會在頁眉中聲明。在這種情況下,頭部中真正聲明的唯一功能是twodigits()notwodigits(),它們被製作爲friend,以提供對index()成員函數的訪問。 index()成員函數只是在調用時間時分配一個可與std::ios_base::iword()一起使用的索引,然後它僅返回此索引。操縱器twodigits()notwodigits()主要設置此索引。如果num_put方面沒有安裝流twodigits()還安裝方面:

std::ostream& twodigits(std::ostream& out) 
{ 
    if (!dynamic_cast<num_put const*>(
      &std::use_facet<std::num_put<char> >(out.getloc()))) { 
     out.imbue(std::locale(out.getloc(), new num_put(out.getloc()))); 
    } 
    out.iword(num_put::index()) = true; 
    return out; 
} 

std::ostream& notwodigits(std::ostream& out) 
{ 
    out.iword(num_put::index()) = false; 
    return out; 
} 

twodigits()操縱分配使用new num_put(out.getloc())num_put方面。它不需要任何清理,因爲在std::locale對象中安裝構面會進行必要的清理。流的原始std::locale使用out.getloc()訪問。它由方面改變。理論上,notwodigits可以恢復原始的std::locale而不是使用標誌。然而,imbue()可能是一個相對昂貴的操作,使用標誌應該便宜很多。當然,如果有很多類似的格式標誌,事情可能會變得不同...

爲了演示操縱器的使用,下面有一個簡單的測試程序。它設置了格式標誌twodigits兩次驗證方面只創建一次(這將是一個有點傻創造std::locale個鏈通過格式化:

int main() 
{ 
    std::cout << "some-int='" << 1 << "' " 
       << twodigits << '\n' 
       << "two-digits1='" << 1 << "' " 
       << "two-digits2='" << 2 << "' " 
       << "two-digits3='" << 3 << "' " 
       << notwodigits << '\n' 
       << "some-int='" << 1 << "' " 
       << twodigits << '\n' 
       << "two-digits4='" << 4 << "' " 
       << '\n'; 
} 
+0

請您詳細介紹一下嗎?這對我來說並不是那麼「明顯」:( – Ali

+0

@Ali:我在手機上輸入了答案,現在我把代碼綁起來...... –

+0

對不起,我不知道,我會等你答案! – Ali

1

爲了方便輸出格式,你可以使用boost::format()sprintf般的格式化選項:

#include <boost/format.hpp> 
#include <iostream> 

int main() { 
    int i1 = 1, i2 = 10, i3 = 100; 
    std::cout << boost::format("%03i %03i %03i\n") % i1 % i2 % i3; 
    // output is: 001 010 100 
} 

小碼的重複,額外的實現努力是微不足道的


如果你只想輸出格式化你的時間戳,你應該明顯地使用strftime()。這就是它是由:

#include <ctime> 
#include <iostream> 

std::string timestamp() { 
    char buf[20]; 
    const char fmt[] = "%Y%m%d%H%M%S"; 
    time_t now = time(0); 
    strftime(buf, sizeof(buf), fmt, localtime(&now)); 
    return buf; 
} 

int main() { 
    std::cout << timestamp() << std::endl; 
} 
+0

謝謝。但是你不需要提升:'snprintf (緩衝區15,「%d%02d%02d%02d%02d%02d」,t.tm_year + 1900,t.tm_mon + 1,t.tm_mday,t.tm_hour,t.tm_min,t.tm_sec);' 。我個人不喜歡C風格格式 – Ali

+0

Upvoted!我不知道'strftime'是標準的一部分,我知道它,但我認爲它只是在POSIX中。 – Ali