3

'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<<でした。呼び出しがあいまいです。


編集4 つのすばらしい回答を得て、おそらく最も単純で最も一般的なものを選びました (つまり、タイムスタンプを扱っているとは想定していません)。実際の問題については、おそらくstd::put_timeorを使用しstrftimeます。

4

6 に答える 6

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<<() の一般的な形式に注意してください。これは最初に作業を行い、次に何かを返します。

于 2012-12-29T19:09:13.393 に答える
2

「明らかな」解決策は、マニピュレータを使用して、必要に応じて s をstd::num_put<char>フォーマットするだけのカスタム ファセットをインストールすることです。int

上記のステートメントは、ソリューションを完全に説明していますが、少しわかりにくいかもしれません。以下は、実際にロジックを実装するためのコードです。最初の要素は、関数の 1 つから派生し、それをオーバーライドするクラスである特別なstd::num_put<char> ファセットです。used ファセットは、( を使用して) ストリームに格納されたフラグを調べて、動作を変更する必要があるかどうかを判断するフィルタリング ファセットです。コードは次のとおりです。std::num_put<char>virtualiword()

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()、値をどのようにフォーマットする必要があるかを決定するメンバー関数です。フラグ infmt.iword(index())がゼロ以外の場合、幅を に2設定し、塗りつぶし文字でフォーマット関数を呼び出します0。とにかく幅はリセットされ、塗りつぶし文字はストリームに保存されません。つまり、クリーンアップの必要はありません。

通常、コードはおそらく別の翻訳単位に存在し、ヘッダーで宣言されません。ヘッダーで実際に宣言される唯一の関数は、メンバー関数へのアクセスを提供するためにこの場合 s にされるtwodigits()andです。メンバー関数は、呼び出されたときに使用できるインデックスを割り当てるだけで、このインデックスを返すだけです。マニピュレータは、主にこのインデックスを設定します。ストリームにファセットがインストールされていない場合は、ファセットもインストールされます。notwodigits()friendindex()index()std::ios_base::iword() twodigits()notwodigits()num_puttwodigits()

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()マニピュレータは、 を使用してファセットnum_putを割り当てnew num_put(out.getloc())ます。std::localeオブジェクトにファセットをインストールすると必要なクリーンアップが行われるため、クリーンアップは必要ありません。std::localeストリームのオリジナルには、 を使用してアクセスしout.getloc()ます。ファセットによって変更されます。理論的には、フラグを使用する代わりにnotwodigitsオリジナルを復元できます。std::localeただし、imbue()比較的高価な操作になる可能性があり、フラグを使用する方がはるかに安価です。もちろん、似たような整形フラグがたくさんあると、違ってくるかもしれませんが…。

マニピュレータの使用方法を示すために、以下に簡単なテスト プログラムを示します。ファセットが 1 回だけ作成されることを確認するために、書式設定フラグをtwodigits2 回設定します (std::locale書式設定を通過する s のチェーンを作成するのは少しばかげています。

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';
}
于 2012-12-29T19:11:55.760 に答える
2

std::setw/std::setfillまたはios_base::width/を使用して整数をフォーマットする以外basic_ios::fillに、日付/時刻オブジェクトをフォーマットする場合は、std::put_time/の使用を検討することをお勧めします。std::gettime

于 2012-12-30T21:43:47.280 に答える
1

便利な出力書式設定のためboost::format()に、sprintf-like 書式設定オプションを使用できます。

#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;
}
于 2012-12-29T20:11:41.350 に答える
0

operator<<(std::ostream& s, int i)そのような関数はすでに存在するため、「あいまい」です。

あなたがする必要があるのは、その関数に競合しない署名を与えることだけです。

于 2012-12-29T18:50:13.153 に答える