C语言 如何计算sprintf将生成的输出长度?

z5btuh9x  于 11个月前  发布在  其他
关注(0)|答案(8)|浏览(108)

目标:将数据序列化为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.我知道我也可以通过log得到整数的长度,但这种方式似乎更好。*
0sgqnhkj

0sgqnhkj1#

由于C语言是简单的语言,因此没有“一次性缓冲区”这样的东西-所有内存管理都在程序员的肩上(有GNU C编译器扩展,但它们不是标准的)。
不能事先知道有多少字符长的整数。
有更简单的解决方案为您的问题. 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, ...)调用。

w51jfk4q

w51jfk4q2#

你可以调用int len = snprintf(NULL, 0, "{data:%d}", 12312)来测试你需要多少空间。
snprintf将打印最多size个字符,其中size是第二个参数,并返回打印整个内容所需的字符数,不包括终止的'\0'。因为传入0,它实际上不会写出任何内容(因此将避免任何空指针异常,这将通过尝试解引用NULL发生),但它仍然会返回适合整个输出所需的长度,您可以使用它来分配缓冲区。
此时,您可以分配并打印到您的缓冲区,记住为尾随的'\0'添加一个缓冲区:

char *buf = malloc(len + 1);
snprintf(buf, len + 1, "{data:%d}", 12312);

字符串

cedebl8k

cedebl8k3#

为了得到长度,你可以这样写:

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

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

anauzrmj

anauzrmj4#

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

x9ybnkn6

x9ybnkn65#

调用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
}

字符串

5jdjgkvh

5jdjgkvh6#

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

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

字符串
应该可以!

amrnrhlw

amrnrhlw7#

可以得到一个可重用的函数,它给出任何格式化字符串的长度,使用 vsnprintf

#include <stdio.h>
#include <stdarg.h>
//...
int fl(const char* format, ...) {
   int n;
   va_list args;
   va_start(args, format);
   n = vsnprintf(NULL,0,format,args);
   va_end(args);
   return n + 1;
}

字符串
示例用法(不使用malloc,而是使用VLA作为缓冲区):

const char* format = "%s times %s\n";
int len = fl(format,"one","two");
if (len>0) {
    char buffer[len];
    sprint(buffer,format,"one","two");
    printf("Size is %d\n", len);
    printf("%s",buffer);
}

jv4diomz

jv4diomz8#

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

相关问题