如何计算sprintf将产生的输出长度?

时间:2015-03-16 21:20:39

标签: c printf

目标:将数据序列化为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这样的一次性写入的实用程序? 像这样的Smth:

#define FORGET_ABOUT_THIS ...
size_t length = sprintf(FORGET_ABOUT_THIS, "{data:%d}",12312);

P.S。我知道我也可以通过日志获得整数的长度,但这种方式看起来更好。

7 个答案:

答案 0 :(得分:17)

由于C是简单语言的地方,没有“一次性缓冲区”这样的东西 - 所有内存管理都在程序员的肩膀上(这些有GNU C编译器扩展,但它们不是标准的)。

  

事先不知道整数是多少个字符。

您的问题有更简单的解决方案。 snprintf知道!

在兼容C99的平台上,使用NULL作为第一个参数调用snprintf:

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, ...)调用。

答案 1 :(得分:12)

您可以致电int len = snprintf(NULL, 0, "{data:%d}", 12312)来测试您需要多少空间。

snprintf将打印最多size个字符,其中size是第二个参数,并返回打印整个内容所需的字符数,不计算终止{ {1}}。因为你传入0,它实际上不会写任何东西(因此将避免通过尝试取消引用'\0')而发生的任何空指针异常,但它仍将返回所需的长度适合整个输出,您可以使用它来分配缓冲区。

此时,您可以分配并打印到缓冲区,还记得为尾随NULL添加一个:

'\0'

答案 2 :(得分:5)

要获得你可以写的长度:

int length = snprintf(NULL, 0, "{data:%d}", 12312);

请注意,返回类型为int。如果出现某种错误,它可能会返回-1。确保您的输入数据不包含可能导致总长度超过INT_MAX的长字符串!

答案 3 :(得分:0)

如果检查性能,那么在没有输出缓冲区的情况下运行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倍。

答案 4 :(得分:0)

Printf支持%n格式参数,这意味着“将%n在输出字符串中的位置写入x参数所指向的int值),因此:

int x;snprintf(NULL, 0, "Message: %s%n", "Error!",&x);

应该可以!

答案 5 :(得分:0)

调用snprintf(nullptr,0,...)确实会返回大小,但是会降低性能,因为它将调用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 \0
}

答案 6 :(得分:-1)

这不是对您的问题的严格答案,但您可能会发现它仍然有用。它不是可移植的,但是如果你在glibc上运行它,你可以简单地使用asprintf(),这将为你做内存分配。