#include <string>
#include <cstdio>
template <typename... Ts>
std::string fmt (const std::string &fmt, Ts... vs)
{
char b;
size_t required = std::snprintf(&b, 0, fmt.c_str(), vs...) + 1;
// See comments: the +1 is necessary, while the first parameter
// can also be set to nullptr
char bytes[required];
std::snprintf(bytes, required, fmt.c_str(), vs...);
return std::string(bytes);
}
_return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).str();
14
在内部使用 vsnprintf() 的C 11解决方案:
#include <stdarg.h> // For va_start, etc.
std::string string_format(const std::string fmt, ...) {
int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code
std::string str;
va_list ap;
while (1) { // Maximum two passes on a POSIX system...
str.resize(size);
va_start(ap, fmt);
int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
va_end(ap);
if (n > -1 && n < size) { // Everything worked
str.resize(n);
return str;
}
if (n > -1) // Needed size returned
size = n + 1; // For null char
else
size *= 2; // Guess at a larger size (OS specific)
}
return str;
}
更安全,更高效(我测试它,它更快)方法:
#include <stdarg.h> // For va_start, etc.
#include <memory> // For std::unique_ptr
std::string string_format(const std::string fmt_str, ...) {
int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
std::unique_ptr<char[]> formatted;
va_list ap;
while(1) {
formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
strcpy(&formatted[0], fmt_str.c_str());
va_start(ap, fmt_str);
final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
va_end(ap);
if (final_n < 0 || final_n >= n)
n += abs(final_n - n + 1);
else
break;
}
return std::string(formatted.get());
}
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <string>
using ::std::string;
#pragma warning(disable : 4996)
#ifndef va_copy
#ifdef _MSC_VER
#define va_copy(dst, src) dst=src
#elif !(__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
#define va_copy(dst, src) memcpy((void*)dst, (void*)src, sizeof(*src))
#endif
#endif
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw() {
int length;
va_list apStrLen;
va_copy(apStrLen, ap);
length = vsnprintf(NULL, 0, format, apStrLen);
va_end(apStrLen);
if (length > 0) {
dst.resize(length);
vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
} else {
dst = "Format error! format: ";
dst.append(format);
}
}
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw() {
va_list ap;
va_start(ap, format);
toString(dst, format, ap);
va_end(ap);
}
///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw() {
string dst;
va_list ap;
va_start(ap, format);
toString(dst, format, ap);
va_end(ap);
return dst;
}
///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw() {
string dst;
toString(dst, format, ap);
return dst;
}
int main() {
int a = 32;
const char * str = "This works!";
string test(toString("\nSome testing: a = %d, %s\n", a, str));
printf(test.c_str());
a = 0x7fffffff;
test = toString("\nMore testing: a = %d, %s\n", a, "This works too..");
printf(test.c_str());
a = 0x80000000;
toString(test, "\nMore testing: a = %d, %s\n", a, "This way is cheaper");
printf(test.c_str());
return 0;
}
STRING.H:
#pragma once
#include <cstdarg>
#include <string>
using ::std::string;
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw();
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw();
///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw();
///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw();
#include <string>
#include <cstdarg>
#include <vector>
// requires at least C++11
const std::string vformat(const char * const zcFormat, ...) {
// initialize use of the variable argument array
va_list vaArgs;
va_start(vaArgs, zcFormat);
// reliably acquire the size
// from a copy of the variable argument array
// and a functionally reliable call to mock the formatting
va_list vaArgsCopy;
va_copy(vaArgsCopy, vaArgs);
const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
va_end(vaArgsCopy);
// return a formatted string without risking memory mismanagement
// and without assuming any compiler or platform specific behavior
std::vector<char> zc(iLen + 1);
std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
va_end(vaArgs);
return std::string(zc.data(), iLen); }
#include <ctime>
#include <iostream>
#include <iomanip>
// demonstration of use
int main() {
std::time_t t = std::time(nullptr);
std::cerr
<< std::put_time(std::localtime(& t), "%D %T")
<< " [debug]: "
<< vformat("Int 1 is %d, Int 2 is %d, Int 3 is %d", 11, 22, 33)
<< std::endl;
return 0; }
30 回答
不幸的是,这里的大多数答案都使用了varargs,除非你使用像GCC的
format
属性这样只能使用文字格式的字符串,否则它本身就是不安全的 . 您可以在以下示例中看到这些函数为何不安全:其中
string_format
是Erik Aronesty的回答 . 此代码编译,但当您尝试运行它时,它很可能会崩溃:可以实现安全
printf
并将其扩展为使用(可变参数)模板格式化std::string
. 这已在fmt library中完成,它提供了sprintf
返回std::string
的安全替代方法:fmt跟踪参数类型,如果类型与格式规范不匹配,则不存在分段错误,只是例外 .
Disclaimer :我是这个图书馆的作者 .
非常简单的解决方案 .
[edit '17 / 8/31]添加可变参数模板'vtspf(..)':
这实际上是一个逗号分隔的版本(相反)有时阻碍
<<
-operators,使用如下:[编辑]适应在Erik Aronesty的答案中使用该技术(上图):
[上一个答案]
这是一个非常晚的答案,但是对于像我一样喜欢'sprintf'的人:我写过并且正在使用以下功能 . 如果你喜欢它,你可以扩展%-options以更接近sprintf的那些;目前那些足以满足我的需求 . 你使用stringf()和stringfappend()与sprintf相同 . 请记住,...的参数必须是POD类型 .
如果您使用的是具有asprintf(3)的系统,则可以轻松将其包装:
从Dacav和pixelpoint's answer接受了这个想法 . 我玩了一下,得到了这个:
使用 sane 编程实践我相信代码应该足够了,但是我仍然对更安全的替代方案持开放态度,这些替代方案仍然很简单并且不需要C 11 .
这是另一个版本,它利用初始缓冲区来防止在初始缓冲区已足够时再次调用
vsnprintf()
.(事实证明,这个版本与Piti Ongmongkolkul's answer类似,只是它不使用
new
和delete[]
,并且在创建std::string
时也指定了一个大小 .这里不使用
new
和delete[]
的想法是暗示在堆上使用堆栈,因为它不需要调用分配和释放函数,但是如果没有正确使用,在某些情况下缓冲溢出可能是危险的(可能是旧的)或者可能只是易受攻击的系统 . 如果这是一个问题,我强烈建议使用new
和delete[]
. 请注意,此处唯一关注的是分配,因为已经使用限制调用vsnprintf()
,因此根据在第二个缓冲区上分配的大小指定限制也会阻止这些限制 . )我对这个非常受欢迎的问题的两分钱 .
引用manpage of printf-like functions:
换句话说,理智的C 11实现应该如下:
它运作得很好:)
仅在C 11中支持变量模板 . 像素点的答案显示了使用较旧编程样式的类似技术 .
奇怪的是,C没有开箱即用的东西 . 他们最近添加了to_string(),我认为这是向前迈出的一大步 . 我想知道他们是否会在
std::string
最终添加.format
运营商...编辑
正如alexk7指出的那样,
std::snprintf
的返回值需要A+1
,因为我们需要为\0
字节留出空间 . 直观地说,在大多数缺少+1
的架构上都会导致required
整数被0
部分覆盖 . 这将在required
评估为std::snprintf
的实际参数后发生,因此效果不应该是可见的 .然而,这个问题可能会改变,例如编译器优化:如果编译器决定使用
required
变量的寄存器怎么办?这种错误有时会导致安全问题 .使用C99 snprintf和C 11
在内部使用
vsnprintf()
的C 11解决方案:更安全,更高效(我测试它,它更快)方法:
fmt_str
按值传递以符合va_start
的要求 .注意:"safer"和"faster"版本在某些系统上不起作用 . 因此两者仍然列出 . 此外,"faster"完全取决于预分配步骤是否正确,否则
strcpy
会使其变慢 .利用C++11 std::snprintf,这将成为一项非常简单和安全的任务 . 我看到很多关于这个问题的答案显然是在C 11之前写的,它使用固定的缓冲长度和vargs,我不建议出于安全性,效率和清晰度的原因 .
上面的代码段在CC0 1.0下获得许可 .
逐行说明:
Aim: 使用
std::snprintf
写入char*
,然后将其转换为std::string
.首先,我们确定char数组的所需长度 .
从cppreference.com:
这意味着所需的大小是字符数 plus one ,因此空终止符将位于所有其他字符之后,并且可以再次被字符串构造函数切断 . @ alexk7在评论中解释了这个问题 .
然后,我们分配一个新的字符数组并将其分配给
std::unique_ptr
. 通常建议这样做,因为您不必再次手动delete
.请注意,这不是一种使用用户定义类型分配
unique_ptr
的安全方法,因为如果构造函数抛出异常,则无法释放内存!之后,我们当然可以将
snprintf
用于其预期用途,并将格式化的字符串写入char[]
,然后创建并返回一个新的std::string
.您可以在行动here中看到一个示例 .
如果您还想在参数列表中使用
std::string
,请查看this gist .Visual Studio用户的其他信息:
如this answer中所述,Microsoft将
std::snprintf
重命名为_snprintf
(是的,没有std::
) . MS进一步将其设置为已弃用并建议使用_snprintf_s,但是_snprintf_s
将不接受缓冲区为零或小于格式化输出,并且如果发生这种情况则不会计算输出长度 . 因此,为了在编译期间摆脱弃用警告,您可以在包含_snprintf
的文件顶部插入the following line:如果您只想要类似printf的语法(不自行调用printf),请查看Boost Format .
我使用vsnprintf编写了自己的,所以它返回字符串而不必创建自己的缓冲区 .
所以你可以像使用它一样
这可以试试 . 简单 . 实际上并没有使用字符串类的细微差别 .
如果缓冲区不足以打印字符串,则可能会出现问题 . 在那里打印格式化的消息之前,您必须确定格式化字符串的长度 . 我自己帮忙(在Windows和Linux上测试GCC),你可以尝试使用它 .
String.cpp:http://pastebin.com/DnfvzyKP
String.h:http://pastebin.com/7U6iCUMa
String.cpp:
STRING.H:
您可以使用iomanip头文件格式化cout中的C输出 . 在使用setprecision,setfill等任何辅助函数之前,请确保包含iomanip头文件 .
这是我过去使用的代码片段,用于打印向量中的平均等待时间,我已经“累积”了 .
以下是我们如何格式化C流的简要说明 . http://www.cprogramming.com/tutorial/iomanip.html
string没有你需要的东西,但是std :: stringstream呢 . 使用stringstream创建字符串,然后提取字符串 . Here是关于您可以做的事情的综合列表 . 例如:
打印双面或浮动时,将给出10位小数的精度 .
这是我在我的程序中用来做这个的代码......这没什么特别的,但它确实有用......注意,你必须根据需要调整你的大小 . MAX_BUFFER对我来说是1024 .
谷歌就是这样做的:StringPrintf(BSD许可证)
和Facebook以非常相似的方式做到:StringPrintf(Apache许可证)
两者都提供了方便的
StringAppendF
.boost::format()提供您想要的功能:
从Boost格式库概要:
我试了一下,用regular expressions . 我为int和const字符串实现了它作为一个例子,但你可以添加任何其他类型(POD类型,但指针可以打印任何东西) .
以下是使用它的示例:
输出:
您可以对底层缓冲区具有写访问权限(直到C 11;请参阅Dietrich Epp的comment) . 您必须先在c-string中执行此操作,然后将其复制到std :: string中:
但我不确定为什么你不会只使用字符串流?我假设你有特定的理由不要这样做:
我通常使用这个:
缺点:并非所有系统都支持vasprint
Tested, Production Quality Answer
这个答案处理符合标准的技术的一般情况 . 在页面底部附近的CppReference.com上给出了相同的方法作为示例 .
Poco Foundation库有一个非常方便的格式函数,它支持格式字符串和值中的std :: string:
Doc:http://pocoproject.org/docs/Poco.html#7308
来源:https://github.com/pocoproject/poco/blob/develop/Foundation/src/Format.cpp
根据答案由Erik Aronesty提供:
这避免了从原始答案中的
.c_str()
的结果中丢弃const
的需要 .要以'sprintf'方式格式化
std::string
,请调用snprintf
(argumentsnullptr
和0
)以获取所需的缓冲区长度 . 使用C 11可变参数模板编写函数,如下所示:编译C 11支持,例如在GCC中:
g++ -std=c++11
用法:
下面稍微修改了@iFreilicht的答案版本,更新为C++14(使用
make_unique
函数而不是原始声明)并添加了对std::string
参数的支持(基于Kenny Kerr article)输出:
如果需要,请随意将此答案与原始答案合并 .
你可以试试这个:
我最喜欢的一个解决方案就是将sprintf直接放到std :: string缓冲区中,然后使缓冲区足够大:
所以,创建std :: string,调整大小,直接访问它的缓冲区......