如何用相同的格式打印一堆整数
How to print a bunch of integers with the same formatting?
我想用'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<<
调用是含糊不清的。
EDIT我得到了4个很棒的答案,我选择了一个可能是最简单和最通用的答案(也就是说,不假设我们正在处理时间戳)。对于实际问题,我可能会使用std::put_time
或strftime
。
在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
格式的{fmt}库更容易:
auto s = fmt::format("{:%Y%m%d%H%M%S}", t);
您需要一个像这样的字符串流代理:
struct stream{
std::ostringstream ss;
stream& operator<<(int i){
ss << std::setw(2) << std::setfill('0') << i;
return *this; // See Note below
}
};
那么你的格式代码将是:
stream 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();
p。注意我的流::operator<<()的一般格式,它先完成它的工作,然后返回一些东西。
"显而易见"的解决方案是使用操纵器来安装一个自定义的std::num_put<char>
facet,它只是按需要格式化int
。
上面的语句可能有点模糊,尽管它完全描述了解决方案。下面是实际实现逻辑的代码。第一个成分是一个特殊的std::num_put<char>
facet,它只是一个从std::num_put<char>
派生的类,并覆盖它的一个virtual
函数。使用的facet是一个过滤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
facet没有为流安装,twodigits()
也会安装facet:
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
facet。它不需要任何清理,因为在std::locale
对象中安装一个facet会进行必要的清理。使用out.getloc()
访问流的原始std::locale
。它由关节面改变。理论上,notwodigits
可以恢复原来的std::locale
,而不是使用一个标志。然而,imbue()
可能是一个相对昂贵的操作,而使用标志应该便宜得多。当然,如果有很多类似的格式化标志,事情可能会变得不同…
twodigits
,以验证只创建了一次facet(创建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';
}
除了使用std::setw
/std::setfill
或ios_base::width
/basic_ios::fill
格式化整数外,如果您想要格式化日期/时间对象,您可能需要考虑使用std::put_time
/std::gettime
为了方便的输出格式化,您可以使用boost::format()
与sprintf
类似的格式化选项:
#include <boost/format.hpp>
#include <iostream>
int main() {
int i1 = 1, i2 = 10, i3 = 100;
std::cout << boost::format("%03i %03i %03in") % 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;
}
operator<<(std::ostream& s, int i)
是"模棱两可的",因为这样的函数已经存在。
你所需要做的就是给这个函数一个不冲突的签名
- 如何使用结构和指针推动和弹出一堆双打
- 如何改进一堆在已知值范围内评估变量的 else-if 条件?
- 当我在结构中包含多个数组时,我的程序会跳过一堆代码
- 编写所需的代码以创建动态一维整数数组
- 我如何将一组整数集变成C 中的3D矢量
- 在共享指针的值中调用 std::swap 调用一堆构造函数和析构函数
- 如何将字符串形式的日期 ( "Dec 25, 2012" ) 转换为一组整数 (12/25/12)?
- 如何查找具有一组整数的函数的'max absolute sum'
- 使用带有.lib和一堆.dll和.h文件的Python CFFI
- 用 c++ 编写一堆类似的 if 语句的漂亮方法
- 当我尝试编译程序时,我遇到了一堆错误,例如:'std::max':找不到匹配的重载函数
- Swig C++ to python:编译一堆.cpp和.h文件
- template元编程:将一堆模板参数相乘
- C++将一组整数变量映射到值的有效方法
- 在.NET中存储一堆恒定值的最佳方法
- 我的一堆函数出现"undefined reference"错误,我不知道为什么
- 制作一堆 int 数组
- 为什么glut.h会在CodeBlocks中弹出一堆未定义的引用
- 在C++中生成一组整数的排列.获取分段错误
- 如何用相同的格式打印一堆整数