我想在'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_time
或strftime
。
答案 0 :(得分: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&lt;&lt;()的一般格式,它首先完成它的工作,然后返回一些东西。
答案 1 :(得分:3)
“显而易见”的解决方案是使用操纵器安装自定义std::num_put<char>
构面,根据需要格式化int
。
上述陈述可能有点神秘,尽管它完全描述了解决方案。下面是实际实现逻辑的代码。第一个成分是一个特殊的std::num_put<char>
facet ,它只是一个派生自std::num_put<char>
并覆盖其virtual
函数之一的类。使用的facet是一个过滤方面,它查看与流一起存储的标志(使用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()
num_put
操纵者使用new num_put(out.getloc())
分配std::locale
方面。它不需要任何清理,因为在std::locale
对象中安装构面会进行必要的清理。使用out.getloc()
访问流的原始notwodigits
。它由facet改变。从理论上讲,std::locale
可以恢复原始imbue()
而不是使用标记。但是,twodigits
可能是一个相对昂贵的操作,使用标志应该便宜很多。当然,如果有很多类似的格式化标志,事情可能会变得不同......
为了演示操纵器的使用,下面有一个简单的测试程序。它设置格式化标志std::locale
两次,以验证facet只创建一次(创建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';
}
链以通过格式化会有点愚蠢:
{{1}}
答案 2 :(得分:3)
在 C++20 中,您将能够以更简洁的方式使用 std::format
执行此操作:
ss << std::format("{}{:02}{:02}{:02}{:02}{:02}",
t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
t.tm_hour, t.tm_min, t.tm_sec);
使用直接支持 tm
格式的 the {fmt} library 更容易:
auto s = fmt::format("{:%Y%m%d%H%M%S}", t);
答案 3 :(得分:2)
除了使用std::setw
/ std::setfill
或ios_base::width
/ basic_ios::fill
格式化整数外,如果要格式化日期/时间对象,可能需要考虑使用{{3 } / std::put_time
答案 4 :(得分: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;
}
答案 5 :(得分:0)
operator<<(std::ostream& s, int i)
“含糊不清”因为这样的功能已经存在。
您需要做的就是为该功能提供一个不冲突的签名。