如何计算 sprintf 将生成的输出长度?
How to calculate the length of output that sprintf will generate?
目标:将数据序列化为JSON。
问题:我无法事先知道整数有多少个字符。
我认为一个很好的方法是使用 sprintf()
size_t length = sprintf(no_buff, "{data:%d}",12312);
char *buff = malloc(length);
snprintf(buff, length, "{data:%d}",12312);
//buff is passed on ...
当然我可以使用像char a[256]
这样的堆栈变量来代替no_buff
。
问题:但是在C中有没有像unix一样的一次性写入的实用程序/dev/null
?
像这样:
#define FORGET_ABOUT_THIS ...
size_t length = sprintf(FORGET_ABOUT_THIS, "{data:%d}",12312);
p.s。我知道我也可以通过日志获取整数的长度,但这种方式似乎更好。
您可以致电int len = snprintf(NULL, 0, "{data:%d}", 12312)
来测试您需要多少space。
snprintf
将最多打印 size
个字符,其中 size
是第二个参数,return 打印整个内容需要多少个字符,不包括终止 '[=15=]'
。因为你传入 0,它实际上不会写出任何东西(因此将避免通过尝试取消引用 NULL
会发生的任何空指针异常),但它仍然会 return 的长度需要适合整个输出,您可以使用它来分配缓冲区。
此时您可以分配并打印到您的缓冲区,记得为尾随 '[=15=]'
:
添加一个缓冲区
char *buf = malloc(len + 1);
snprintf(buf, len + 1, "{data:%d}", 12312);
为了得到你可以写的长度:
int length = snprintf(NULL, 0, "{data:%d}", 12312);
注意 return 类型是 int
。如果出现某种错误,它可能 return -1
。确保您的输入数据不包含可能导致总长度超过 INT_MAX
!
的长字符串
因为 C 是简单的语言,所以没有 "disposable buffers" 这样的东西——所有的内存管理都在程序员的肩上(有针对这些的 GNU C 编译器扩展,但它们不是标准的)。
cant know beforehand how many chars long the integer is.
您的问题有更简单的解决方案。 snprintf
知道了!
在 C99 兼容平台上调用 snprintf 并将 NULL 作为第一个参数:
ssize_t bufsz = snprintf(NULL, 0, "{data:%d}",12312);
char* buf = malloc(bufsz + 1);
snprintf(buf, bufsz + 1, "{data:%d}",12312);
...
free(buf);
在较旧的 Visual Studio 版本(具有非 C99 兼容的 CRT)中,使用 _scprintf
而不是 snprintf(NULL, ...)
调用。
严格来说,这并不是您问题的答案,但您可能会发现它对您有所帮助。它不可移植,但如果你在 glibc 上 运行 这个,你可以简单地使用 asprintf()
来代替,它将为你分配内存。
如果您检查性能,您会 运行在没有输出缓冲区的情况下使用 snprintf 将花费与完整调用大致相同的时间。
所以我建议您使用较小的缓冲区以防万一,并且只有在返回的大小超过缓冲区大小时才再次调用它。
这使用 C++ 的 std::string
,但我想您可以根据需要调整它。
std::string format(const char* format, ...) {
va_list args;
va_start(args, format);
char smallBuffer[1024];
int size = vsnprintf(smallBuffer, sizeof smallBuffer, format, args);
va_end(args);
if (size < sizeof smallBuffer)
return std::string(smallBuffer);
char buffer[size + 1]; /* maybe malloc if it's too big */
va_start(args, format);
vsnprintf(buffer, sizeof buffer, format, args);
va_end(args);
return std::string(buffer);
}
对于 1k 以下的字符串,与较长的字符串相比,此代码 运行 快 2 倍。
Printf支持%n格式参数,即“将%n在输出字符串中的位置写入x参数指向的int值”,所以:
int x;snprintf(NULL, 0, "Message: %s%n", "Error!",&x);
应该可以!
调用 snprintf(nullptr, 0, ...) 会 return 大小,但它有性能损失,因为它会调用 IO_str_overflow 并且速度很慢。
如果您确实关心性能,您可以预先分配一个虚拟缓冲区并将其指针和大小传递给::snprintf。它将比 nullptr 版本快几倍。
template<typename ...Args>
size_t get_len(const char* format, Args ...args) {
static char dummy[4096]; // you can change the default size
return ::snprintf(dummy, 4096, format, args...) + 1; // +1 for [=10=]
}
目标:将数据序列化为JSON。
问题:我无法事先知道整数有多少个字符。
我认为一个很好的方法是使用 sprintf()
size_t length = sprintf(no_buff, "{data:%d}",12312);
char *buff = malloc(length);
snprintf(buff, length, "{data:%d}",12312);
//buff is passed on ...
当然我可以使用像char a[256]
这样的堆栈变量来代替no_buff
。
问题:但是在C中有没有像unix一样的一次性写入的实用程序/dev/null
?
像这样:
#define FORGET_ABOUT_THIS ...
size_t length = sprintf(FORGET_ABOUT_THIS, "{data:%d}",12312);
p.s。我知道我也可以通过日志获取整数的长度,但这种方式似乎更好。
您可以致电int len = snprintf(NULL, 0, "{data:%d}", 12312)
来测试您需要多少space。
snprintf
将最多打印 size
个字符,其中 size
是第二个参数,return 打印整个内容需要多少个字符,不包括终止 '[=15=]'
。因为你传入 0,它实际上不会写出任何东西(因此将避免通过尝试取消引用 NULL
会发生的任何空指针异常),但它仍然会 return 的长度需要适合整个输出,您可以使用它来分配缓冲区。
此时您可以分配并打印到您的缓冲区,记得为尾随 '[=15=]'
:
char *buf = malloc(len + 1);
snprintf(buf, len + 1, "{data:%d}", 12312);
为了得到你可以写的长度:
int length = snprintf(NULL, 0, "{data:%d}", 12312);
注意 return 类型是 int
。如果出现某种错误,它可能 return -1
。确保您的输入数据不包含可能导致总长度超过 INT_MAX
!
因为 C 是简单的语言,所以没有 "disposable buffers" 这样的东西——所有的内存管理都在程序员的肩上(有针对这些的 GNU C 编译器扩展,但它们不是标准的)。
cant know beforehand how many chars long the integer is.
您的问题有更简单的解决方案。 snprintf
知道了!
在 C99 兼容平台上调用 snprintf 并将 NULL 作为第一个参数:
ssize_t bufsz = snprintf(NULL, 0, "{data:%d}",12312);
char* buf = malloc(bufsz + 1);
snprintf(buf, bufsz + 1, "{data:%d}",12312);
...
free(buf);
在较旧的 Visual Studio 版本(具有非 C99 兼容的 CRT)中,使用 _scprintf
而不是 snprintf(NULL, ...)
调用。
严格来说,这并不是您问题的答案,但您可能会发现它对您有所帮助。它不可移植,但如果你在 glibc 上 运行 这个,你可以简单地使用 asprintf()
来代替,它将为你分配内存。
如果您检查性能,您会 运行在没有输出缓冲区的情况下使用 snprintf 将花费与完整调用大致相同的时间。
所以我建议您使用较小的缓冲区以防万一,并且只有在返回的大小超过缓冲区大小时才再次调用它。
这使用 C++ 的 std::string
,但我想您可以根据需要调整它。
std::string format(const char* format, ...) {
va_list args;
va_start(args, format);
char smallBuffer[1024];
int size = vsnprintf(smallBuffer, sizeof smallBuffer, format, args);
va_end(args);
if (size < sizeof smallBuffer)
return std::string(smallBuffer);
char buffer[size + 1]; /* maybe malloc if it's too big */
va_start(args, format);
vsnprintf(buffer, sizeof buffer, format, args);
va_end(args);
return std::string(buffer);
}
对于 1k 以下的字符串,与较长的字符串相比,此代码 运行 快 2 倍。
Printf支持%n格式参数,即“将%n在输出字符串中的位置写入x参数指向的int值”,所以:
int x;snprintf(NULL, 0, "Message: %s%n", "Error!",&x);
应该可以!
调用 snprintf(nullptr, 0, ...) 会 return 大小,但它有性能损失,因为它会调用 IO_str_overflow 并且速度很慢。
如果您确实关心性能,您可以预先分配一个虚拟缓冲区并将其指针和大小传递给::snprintf。它将比 nullptr 版本快几倍。
template<typename ...Args>
size_t get_len(const char* format, Args ...args) {
static char dummy[4096]; // you can change the default size
return ::snprintf(dummy, 4096, format, args...) + 1; // +1 for [=10=]
}