如何使用具有类似 printf 格式的 C++ std::ostream?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15106102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use C++ std::ostream with printf-like formatting?
提问by Eonil
I am learning C++. cout
is an instance of std::ostream
class.
How can I print a formatted string with it?
我正在学习 C++。cout
是std::ostream
类的一个实例。如何用它打印格式化的字符串?
I can still use printf
, but I want to learn a proper C++ method which can take advantage of all C++ benefits. I think this should be possible with std::ostream
, but I can't find the proper way.
我仍然可以使用printf
,但我想学习一种适当的 C++ 方法,它可以利用所有 C++ 的优点。我认为这应该是可能的std::ostream
,但我找不到正确的方法。
采纳答案by jogojapan
The only thing you can do with std::ostream
directly is the well known <<
-syntax:
您唯一可以std::ostream
直接使用的是众所周知的<<
-syntax:
int i = 0;
std::cout << "this is a number: " << i;
And there are various IO manipulatorsthat can be used to influence the formatting, number of digits, etc. of integers, floating point numbers etc.
并且有各种IO 操纵器可用于影响整数、浮点数等的格式、位数等。
However, that is not the same as the formatted strings of printf
. C++11 does not include any facility that allows you to use string formatting in the same way as it is used with printf
(except printf
itself, which you can of course use in C++ if you want).
但是,这与printf
. C++11 不包含任何允许您以与使用相同的方式使用字符串格式化的工具printf
(除了printf
它本身,如果需要,您当然可以在 C++ 中使用它)。
In terms of libraries that provide printf
-style functionality, there is boost::format
, which enables code such as this (copied from the synopsis):
在提供printf
样式功能的库方面,有boost::format
,它可以启用这样的代码(从概要中复制):
std::cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;
Also note that there is a proposal for inclusionof printf
-style formatting in a future version of the Standard. If this gets accepted, syntax such as the below may become available:
还要注意的是有一个列入提议的printf
在标准的未来版本样式的格式。如果这被接受,则可能会使用以下语法:
std::cout << std::putf("this is a number: %d\n",i);
回答by Anand Venkataraman
This is an idiom I have gotten used to. Hopefully it helps:
这是我已经习惯的成语。希望它有帮助:
// Hacky but idiomatic printf style syntax with c++ <<
#include <cstdlib> // for sprintf
char buf[1024]; sprintf(buf, "%d score and %d years ago", 4, 7);
cout << string(buf) <<endl;
&
&
回答by user3754430
To implement printf one could use c++11 template parameters:
要实现 printf 可以使用 c++11 模板参数:
#include <iostream>
#include <string>
inline std::ostream & mprintf(std::ostream & ostr, const char * fstr) throw()
{
return ostr << fstr;
}
template<typename T, typename... Args>
std::ostream & mprintf(std::ostream & ostr,
const char * fstr, const T & x) throw()
{
size_t i=0;
char c = fstr[0];
while (c != '%')
{
if(c == 0) return ostr; // string is finished
ostr << c;
c = fstr[++i];
};
c = fstr[++i];
ostr << x;
if(c==0) return ostr; //
// print the rest of the stirng
ostr << &fstr[++i];
return ostr;
}
template<typename T, typename... Args>
std::ostream & mprintf(std::ostream & ostr,
const char * fstr, const T & x, Args... args) throw()
{
size_t i=0;
char c = fstr[0];
while (c != '%')
{
if(c == 0) return ostr; // string is finished
ostr << c;
c = fstr[++i];
};
c = fstr[++i];
ostr << x;
if(c==0) return ostr; // string is finished
return mprintf(ostr, &fstr[++i], args...);
}
int main()
{
int c = 50*6;
double a = 34./67.;
std::string q = "Hello!";
// put only two arguments
// the symbol after % does not matter at all
mprintf(std::cout, "%f + %f = %a \n", c, a);
// print string object: for real printf one should write q.c_str()
mprintf(std::cout, "message: \"%s\". \n", q);
// the last argument will be ignored
mprintf(std::cout, "%z + %f\n", (long)a, 12, 544 );
}
Output
输出
300 + 2 = %a
message: "Hello!".
2 + 12
This a very simple code and it can be improved.
这是一个非常简单的代码,可以改进。
1) The advantage is that it uses << to print objects to the stream, so you can put arbitrary arguments that can be output via <<.
1)优点是它使用<<将对象打印到流,因此您可以放置可以通过<<输出的任意参数。
2) It ignores the type of the argument in the formatted string: after % can stand arbitrary symbol even a space. The output stream decides how to print the corresponding object. It also compatible with printf.
2) 它忽略格式化字符串中参数的类型:在 % 之后可以代表任意符号,甚至是空格。输出流决定如何打印相应的对象。它还与 printf 兼容。
3) A disadvantage is that it can not print the percent symbol '%', one need to slightly improve the code.
3)缺点是不能打印百分号'%',需要稍微改进一下代码。
4) It can not print formatted numbers, like %4.5f
4) 它不能打印格式化的数字,如 %4.5f
5) If the number of arguments is less than predicted by formatted string, then the function just print the rest of the string.
5) 如果参数数量少于格式化字符串预测的数量,则该函数只打印字符串的其余部分。
6) If the number of arguments is greater than predicted by formatted string, then the remained arguments are ignored
6) 如果参数数量大于格式化字符串预测的数量,则忽略剩余的参数
One can improve the code to make 2)-6) to fully mimic the printf behaviour. However, if you follow the rules of printf, then only 3) and 4) need essentially to be fixed.
可以改进代码使 2)-6) 完全模仿 printf 行为。但是,如果您遵循 printf 的规则,那么基本上只需要修复 3) 和 4)。
回答by Zephyr
Field Width
Setting field width is very simple. For each variable, simply precede it with "setw(n)". Like this:
#include <iostream> #include <iomanip> using namespace std; int main() { const int max = 12; const int width = 6; for(int row = 1; row <= max; row++) { for(int col = 1; col <= max; col++) { cout << setw(width) << row * col; } cout << endl; } return 0; }
Notice how "setw(n)" controls the field width, so each number is printed inside a field that stays the same width regardless of the width of the number itself.
字段宽度
设置字段宽度非常简单。对于每个变量,只需在它前面加上“setw(n)”。像这样:
#include <iostream> #include <iomanip> using namespace std; int main() { const int max = 12; const int width = 6; for(int row = 1; row <= max; row++) { for(int col = 1; col <= max; col++) { cout << setw(width) << row * col; } cout << endl; } return 0; }
请注意“setw(n)”如何控制字段宽度,因此每个数字都打印在一个保持相同宽度的字段内,而不管数字本身的宽度如何。
-- From "Programming/C++ tutorial" by P. Lutus.
-- 来自P. Lutus 的“编程/C++ 教程”。
回答by serup
I suggest using ostringstream instead of ostream see following example :
我建议使用 ostringstream 而不是 ostream 参见以下示例:
#include <vector>
#include <string>
#include <iostream>
#include "CppUnitTest.h"
#define _CRT_NO_VA_START_VALIDATION
std::string format(const std::string& format, ...)
{
va_list args;
va_start(args, format);
size_t len = std::vsnprintf(NULL, 0, format.c_str(), args);
va_end(args);
std::vector<char> vec(len + 1);
va_start(args, format);
std::vsnprintf(&vec[0], len + 1, format.c_str(), args);
va_end(args);
return &vec[0];
}
example usage:
用法示例:
std::ostringstream ss;
ss << format("%s => %d", "Version", Version) << std::endl;
Logger::WriteMessage(ss.str().c_str()); // write to unit test output
std::cout << ss.str() << std::endl; // write to standard output
回答by RFST
Sample output:
示例输出:
2017-12-20T16:24:47,604144+01:00 Hello, World!
Code (with put_printf usage demonstrated in put_timestamp):
代码(在 put_timestamp 中演示了 put_printf 用法):
#include <assert.h>
#include <chrono>
#include <iomanip>
#include <iostream>
class put_printf {
static constexpr size_t failed = std::numeric_limits<size_t>::max(); // for any explicit error handling
size_t stream_size; // excluding '// Reasons for the name "put_printf" (and not "putf" after all):
// - put_printf is self-documenting, while using the naming pattern also seen in std::put_time;
// - it is not clear whether the proposed std::putf would support exactly the same format syntax;
// - it has a niche purpose, so a longer name is not an objection, and for frequent local uses
// it is easy enough to declare an even shorter "typedef put_printf pf;" or so.
// Evaluation of delegating to vsnprintf() with intermediate buffer:
// (+) identical result without implementation and/or maintenance issues,
// (?) succeeds or fails as a whole, no output of successful prefix before point of failure
// (-) (total output size limited to INT_MAX-1)
// (-) overhead (TODO: optimal buf_stack size considering cache and VM page locality?)
// Error handling (an STL design problem?):
// - std::cout.setstate(std::ios_base::failbit) discards further std::cout output (stdout still works),
// so, to be aware of an error in business logic yet keep on trucking in diagnostics,
// should there be separate classes, or a possibility to plug in an error handler, or what?
// - should the basic or default error handling print a diagnostic message? throw an exception?
// TODO: could a function "int ostream_printf(std::ostream& os, const char *format, ...)"
// first try to write directly into os.rdbuf() before using buf_stack and buf_heap,
// and would that significantly improve performance or not?
'; on error set to 0 or to "failed"
char buf_stack[2048+1]; // MAY be any size that fits on the stack (even 0), SHOULD be (just) large enough for most uses (including 'std::cout << putf(const char *format, ...); //Same format as C printf(3)
')
std::unique_ptr<char[]> buf_heap; // only used if the output doesn't fit in buf_stack
public:
explicit put_printf(const char *format, ...)
#if __GNUC__
__attribute__ ((format (printf, 2, 3))) // most compelling reason for not using a variadic template; parameter 1 is implied "this"
#endif
{
va_list args;
va_start(args, format);
const int res = vsnprintf(buf_stack, sizeof(buf_stack), format, args);
va_end(args);
if (res < 0) { // easily provoked, e.g., with "%02147483646i\n", i.e., more than INT_MAX-1 significant characters (only observed, no guarantee seen)
stream_size = failed;
} else if (res < sizeof(buf_stack)) { // preferred path
stream_size = res;
} else { // not artificially constrained
try {
const size_t buf_size = static_cast<size_t>(res) + 1; // avoids relying on "res < INT_MAX" (only observed, no guarantee seen)
buf_heap.reset(new char[buf_size]); // observed to work even beyond INT_MAX=2^32-1 bytes
va_start(args, format);
if (vsnprintf(buf_heap.get(), buf_size, format, args) == res) stream_size = res;
else stream_size = failed; // can't happen
va_end(args);
} catch (const std::bad_alloc&) { // insufficient free heap space (or an environment-specific constraint?)
stream_size = failed;
}
}
}
friend std::ostream& operator<<(std::ostream& os, const put_printf& self) {
if (self.stream_size == failed) {
// (placeholder for any explicit error handling)
return os;
} else {
// using write() rather than operator<<() to avoid a separate scan for '#define _GNU_SOURCE
#include <cstdarg>
#include <iostream>
#include <cstdio>
struct putf_r{
char *s;
};
putf_r putf(const char *fmt, ...){
va_list ap;
va_start(ap, fmt);
putf_r a;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
vasprintf(&a.s, fmt, ap);
#pragma GCC diagnostic pop
va_end(ap);
return a;
}
std::ostream& operator<<(std::ostream& os, putf_r a){
os<<a.s;
free(a.s);
return os;
}
int main(){
std::cout << putf("%3d\n", 23) << putf("%a\n", 256.);
}
' or unintentional truncation at any internal '#include <stdio>
#include <stdarg>
#include <stdlib>
#include <iostream>
#pragma hdrstop
using namespace std;
char* print(char* fmt, ...)
{
static char buffer[80] = "";
va_list argptr;
va_start(argptr,fmt);
vsprintf(buffer, fmt, argptr);
va_end(argptr);
return buffer;
}
#pragma argsused
int main(int argc, char* argv[])
{
cout << print("\n%06d\n%6d\n%6d\n%010.3f",1,12,123,123.456);
system("PAUSE>NUL");
return 0;
}
' character
return os.write((self.buf_heap ? self.buf_heap.get() : self.buf_stack), self.stream_size);
}
}
};
class put_timestamp {
const bool basic = false;
const bool local = true;
public:
friend std::ostream& operator<<(std::ostream& os, const put_timestamp& self) {
const auto now = std::chrono::system_clock::now();
const std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
struct tm tm; if ((self.local ? localtime_r(&now_time_t, &tm) : gmtime_r(&now_time_t, &tm)) == nullptr) return os; // TODO: explicit error handling?
static_assert(4 <= sizeof(int), "");
const int microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch() % std::chrono::seconds(1)).count();
assert(0 <= microseconds && microseconds < 1000000); // TODO: (how) do we know?
// TODO: doesn't "point" in "decimal_point()" imply "dot"/"full stop"/"period", unlike an obviously neutral term like "mark"/"separator"/"sign"?
const char decimal_sign = std::use_facet<std::numpunct<char>>(os.getloc()).decimal_point() == '.' ? '.' : ','; // full stop accepted, comma preferred
// TODO: all well and good for a locale-specific decimal sign, but couldn't the locale also upset microseconds formatting by grouping digits?
os << std::put_time(&tm, self.basic ? "%Y%m%dT%H%M%S" : "%FT%T") << put_printf("%c%06i", decimal_sign, microseconds);
if (! self.local) return os << "Z";
const int tz_minutes = std::abs(static_cast<int>(tm.tm_gmtoff)) / 60;
return os << put_printf(self.basic ? "%c%02i%02i" : "%c%02i:%02i", 0 <= tm.tm_gmtoff ? '+' : '-', tz_minutes / 60, tz_minutes % 60);
}
};
int main() {
// testing decimal sign
///std::cout.imbue(std::locale("en_GB"));
///std::cout.imbue(std::locale("fr_FR"));
std::cout << put_timestamp() << " Hello, World!\n";
#if 0
typedef put_printf pf; // just to demo local abbreviation
std::cout << "1: " << pf("%02147483646i\n" , 1 ) << std::endl; // res < 0
std::cout << "2: " << pf("%02147483643i%i\n", 1, 100) << std::endl; // res < 0
std::cout << "3: " << pf("%02147483643i%i\n", 1, 10) << std::endl; // works
std::cout << "4: " << pf("%02147483646i" , 1 ) << std::endl; // works
#endif
return 0;
}
Comments about put_printf:
关于 put_printf 的评论:
##代码##回答by WingTillDie
I wrote independently but came up with answer similar to user3283405
我独立写作,但想出了类似于 user3283405 的答案
My solution uses vasprintf() to acheive formatting, and uses operator overloading of << of std::ostream to free the memory in right place.
我的解决方案使用 vasprintf() 来实现格式化,并使用 std::ostream 的 << 运算符重载以在正确的位置释放内存。
Usage:
用法:
##代码##Code:
代码:
##代码##Note that compiler doesn't check format inside putf(), so compiler flag -Wformat-nonliteral will not warn for suspicious code in putf() and you need to care uncontrolled format stringproblem by yourself.
Detailed info can be found on GitHub
请注意,编译器不会检查 putf() 内部的格式,因此编译器标志 -Wformat-nonliteral 不会对 putf() 中的可疑代码发出警告,您需要自己处理不受控制的格式字符串问题。
详细信息可以在GitHub上找到
回答by user3283405
When I need both the typesafety of cout and the quick and easy formatting of simple variables of printf(), I mix the two like this. This is an ugly fix, but it gets things done for me when I need to output things like "02/07/2014 10:05am" together with some more complex entities:
当我需要 cout 的类型安全性和 printf() 的简单变量的快速简便的格式化时,我将两者混合在一起。这是一个丑陋的修复,但是当我需要输出诸如“02/07/2014 10:05am”之类的内容以及一些更复杂的实体时,它可以为我完成工作:
##代码##