我必须用sprintf格式化std::string,并将其发送到文件流。我该怎么做呢?
当前回答
更新了一些答案,不同的是-函数将正确接受std::string为%s
namespace format_helper
{
template <class Src>
inline Src cast(Src v)
{
return v;
}
inline const char *cast(const std::string& v)
{
return v.c_str();
}
};
template <typename... Ts>
inline std::string stringfmt (const std::string &fmt, Ts&&... vs)
{
using namespace format_helper;
char b;
size_t required = std::snprintf(&b, 0, fmt.c_str(), cast(std::forward<Ts>(vs))...);//not counting the terminating null character.
std::string result;
//because we use string as container, it adds extra 0 automatically
result.resize(required , 0);
//and snprintf will use n-1 bytes supplied
std::snprintf(const_cast<char*>(result.data()), required + 1, fmt.c_str(), cast(std::forward<Ts>(vs))...);
return result;
}
生活:http://cpp.sh/5ajsv
其他回答
到目前为止,所有的答案似乎都有一个或多个这样的问题:(1)它可能无法在vc++上工作(2)它需要额外的依赖,如boost或fmt(3)它太复杂的自定义实现,可能没有经过很好的测试。
下面的代码解决了上述所有问题。
#include <string>
#include <cstdarg>
#include <memory>
std::string stringf(const char* format, ...)
{
va_list args;
va_start(args, format);
#ifndef _MSC_VER
//GCC generates warning for valid use of snprintf to get
//size of result string. We suppress warning with below macro.
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
size_t size = std::snprintf(nullptr, 0, format, args) + 1; // Extra space for '\0'
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
std::unique_ptr<char[]> buf(new char[ size ] );
std::vsnprintf(buf.get(), size, format, args);
return std::string(buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
#else
int size = _vscprintf(format, args);
std::string result(++size, 0);
vsnprintf_s((char*)result.data(), size, _TRUNCATE, format, args);
return result;
#endif
va_end(args);
}
int main() {
float f = 3.f;
int i = 5;
std::string s = "hello!";
auto rs = stringf("i=%d, f=%f, s=%s", i, f, s.c_str());
printf("%s", rs.c_str());
return 0;
}
注:
Separate VC++ code branch is necessary because VC++ has decided to deprecate snprintf which will generate compiler warnings for other highly voted answers above. As I always run in "warnings as errors" mode, its no go for me. The function accepts char * instead of std::string. This because most of the time this function would be called with literal string which is indeed char *, not std::string. In case you do have std::string as format parameter, then just call .c_str(). Name of the function is stringf instead of things like string_format to keepup with printf, scanf etc. It doesn't address safety issue (i.e. bad parameters can potentially cause seg fault instead of exception). If you need this then you are better off with boost or fmt libraries. My preference here would be fmt because it is just one header and source file to drop in the project while having less weird formatting syntax than boost. However both are non-compatible with printf format strings so below is still useful in that case. The stringf code passes through GCC strict mode compilation. This requires extra #pragma macros to suppress false positives in GCC warnings.
以上代码已在,
GCC 4.9.2 11 / c++ / C + + 14 vc++编译器19.0 铿锵声3.7.0
如果你在一个有asprintf(3)的系统上,你可以很容易地对它进行包装:
#include <iostream>
#include <cstdarg>
#include <cstdio>
std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
std::string format(const char *fmt, ...)
{
std::string result;
va_list ap;
va_start(ap, fmt);
char *tmp = 0;
int res = vasprintf(&tmp, fmt, ap);
va_end(ap);
if (res != -1) {
result = tmp;
free(tmp);
} else {
// The vasprintf call failed, either do nothing and
// fall through (will return empty string) or
// throw an exception, if your code uses those
}
return result;
}
int main(int argc, char *argv[]) {
std::string username = "you";
std::cout << format("Hello %s! %d", username.c_str(), 123) << std::endl;
return 0;
}
我喜欢的一个解决方案是,在使缓冲区足够大之后,用sprintf直接在std::string缓冲区中执行此操作:
#include <string>
#include <iostream>
using namespace std;
string l_output;
l_output.resize(100);
for (int i = 0; i < 1000; ++i)
{
memset (&l_output[0], 0, 100);
sprintf (&l_output[0], "\r%i\0", i);
cout << l_output;
cout.flush();
}
因此,创建std::string,调整它的大小,直接访问它的缓冲区…
下面是@iFreilicht答案的稍微修改版本,更新到c++ 14(使用make_unique函数而不是原始声明),并增加了对std::string参数的支持(基于Kenny Kerr的文章)
#include <iostream>
#include <memory>
#include <string>
#include <cstdio>
template <typename T>
T process_arg(T value) noexcept
{
return value;
}
template <typename T>
T const * process_arg(std::basic_string<T> const & value) noexcept
{
return value.c_str();
}
template<typename ... Args>
std::string string_format(const std::string& format, Args const & ... args)
{
const auto fmt = format.c_str();
const size_t size = std::snprintf(nullptr, 0, fmt, process_arg(args) ...) + 1;
auto buf = std::make_unique<char[]>(size);
std::snprintf(buf.get(), size, fmt, process_arg(args) ...);
auto res = std::string(buf.get(), buf.get() + size - 1);
return res;
}
int main()
{
int i = 3;
float f = 5.f;
char* s0 = "hello";
std::string s1 = "world";
std::cout << string_format("i=%d, f=%f, s=%s %s", i, f, s0, s1) << "\n";
}
输出:
i = 3, f = 5.000000, s = hello world
如果需要,可以随意将这个答案与原始答案合并。
为了以'sprintf'方式格式化std::string,调用snprintf(参数nullptr和0)来获得所需的缓冲区长度。使用c++ 11可变模板编写函数,如下所示:
#include <cstdio>
#include <string>
#include <cassert>
template< typename... Args >
std::string string_sprintf( const char* format, Args... args ) {
int length = std::snprintf( nullptr, 0, format, args... );
assert( length >= 0 );
char* buf = new char[length + 1];
std::snprintf( buf, length + 1, format, args... );
std::string str( buf );
delete[] buf;
return str;
}
使用c++11支持编译,例如在GCC: g++ -std=c++11中编译
用法:
std::cout << string_sprintf("%g, %g\n", 1.23, 0.001);
推荐文章
- 如何构建和使用谷歌TensorFlow c++ api
- 我应该如何从字符串中删除所有的前导空格?- - - - - -斯威夫特
- 将整数转换为字符串,以逗号表示千
- 将JavaScript字符串中的多个空格替换为单个空格
- printf()和puts()在C语言中的区别是什么?
- 断言是邪恶的吗?
- 下面这些短语在c++中是什么意思:0 -,default-和value-initialization?
- 在STL地图中,使用map::insert比[]更好吗?
- C++ Linux的想法?
- jQuery -替换字符串中某个字符的所有实例
- Base64长度计算?
- 如何为Fedora安装g++ ?
- 使用split("|")按管道符号拆分Java字符串
- Std::cin输入空格?
- c++标准是否要求iostreams的性能很差,或者我只是在处理一个糟糕的实现?