为了方便使用,我想编写类似于sprintf的格式化函数,只需返回std :: string,如下所示:
std::string format_string(const char* format, ...)
我可以在那里使用vsnprintf
但是有问题 - 我事先并不知道临时缓冲区应该有多长。在微软有功能_vscprintf
可以做到,但我认为它不可移植?
一个选项是让临时缓冲区启动一些已知大小,然后在vsnprintf
看到它不够时增加它。有更好的方法吗?感谢
P.S。请不要Boost给出答案。我知道Boost,但我很好奇如何实现它。
答案 0 :(得分:3)
C99也引入了snprintf和vsnprintf
。 (v)snprintf
有几个可移植的开源实现,例如this one。后者还实现了动态分配存储的vasprintf
。
另请考虑C++ Format library提供类似于Boost格式的安全printf
实现,但要快得多。
答案 1 :(得分:3)
一个选项是让临时缓冲区启动一些已知大小,然后在
vsnprintf
看到它不够时增加它。有更好的方法吗?感谢
您可以使用vasprintf()
,但这会产生不必要的堆分配 - 平均来说不太可能更快。使用alloca
可以避免堆。或者,您可以直接写入返回的string
:NRVO应该避免复制,并且从C ++ 11开始,移动语义会将成本sans-NRVO限制为几个指针交换。
#include <cstdio>
#include <cstdarg>
#include <alloca.h>
#include <string>
#include <iostream>
std::string stringf(const char* format, ...)
{
va_list arg_list;
va_start(arg_list, format);
// SUSv2 version doesn't work for buf NULL/size 0, so try printing
// into a small buffer that avoids the double-rendering and alloca path too...
char short_buf[256];
const size_t needed = vsnprintf(short_buf, sizeof short_buf,
format, arg_list) + 1;
if (needed <= sizeof short_buf)
return short_buf;
// need more space...
// OPTION 1
std::string result(needed, ' ');
vsnprintf(result.data(), needed, format, arg_list);
return result; // RVO ensures this is cheap
OR
// OPTION 2
char* p = static_cast<char*>(alloca(needed)); // on stack
vsnprintf(p, needed, format, arg_list);
return p; // text copied into returned string
}
int main()
{
std::string s = stringf("test '%s', n %8.2f\n", "hello world", 3.14);
std::cout << s;
}
更简单且最初更快的选项是:
std::string result(255, ' '); // 255 spaces + NUL
const size_t needed = vsnprintf(result.data(), result.size() + 1,
format, arg_list);
result.resize(needed); // may truncate, leave or extend...
if (needed > 255) // needed doesn't count NUL
vsnprintf(result.data(), needed + 1, format, arg_list);
return result;
潜在的问题是你要分配至少256个字符,但是要存储的实际文本很短:这可能会增加你的内存/缓存相关性能。您可以使用[shrink_to_fit
] http://en.cppreference.com/w/cpp/string/basic_string/shrink_to_fit)解决问题,但标准并不要求它实际执行任何操作(要求是“非绑定”)。如果您最终必须复制到一个新的精确大小的字符串,您可能也使用了本地char数组。
答案 2 :(得分:0)
如果你不关心使用非标准功能(即:对任何平台使用不同的功能,就像我从你的问题中得到的那样),并且想要快速的工作,你会发现{{1}和GNU扩展中的asprintf
(也就是它:C也不是POSIX,但是由GCC和glibc支持)。
它们的工作方式类似于vasprintf
和printf
,但需要注意分配缓冲区内存以简化工作。
vsprintf
您可能会在任何系统上找到类似的功能。对于其他人,您可以编写一些代码来分配一些缓冲区并将其传递给int asprintf( char **strp, const char *fmt, ... );
int vasprintf( char **strp, const char *fmt, va_list ap );
。
答案 3 :(得分:0)
执行此操作的有效方法是自己编写整个函数。也就是说,不要转发到其他类似printf
的函数,而是自己解析并打印所有参数。扫描整个格式字符串,并检查参数以确定必要的缓冲区大小。随后,打印到该缓冲区
这不是一个全有或全无的建议;您仍然可以对所选类型使用sprintf
。例如。当您的输入格式字符串包含sprintf(buf, "%6.4f", dbltemp);
参数时,使用%6.4f
可能更容易,但%s
可以更好地处理(简单的memcpy)。