将未知长度的格式化数据写入字符串(C编程)

时间:2009-12-06 04:25:19

标签: c string printf

以下C函数:

int sprintf ( char * str, const char * format, ... );

将格式化数据写入字符串。作为str传递的数组的大小应该足以包含整个格式化的字符串。但是,如果格式化字符串的长度未提前知道怎么办?如何使用此函数(或类似的其他函数)写入长度未知的格式化数据?

例如:

#include <stdio.h>

int main ()
{
  char buffer [13];
  int n, a=5, b=3;
  n=sprintf (buffer, "%d plus %d is %d", a, b, a+b);
  printf ("[%s] is a %d char long string\n",buffer,n);
  return 0;
}

缓冲区需要为13或更大才能使其正常工作。如果字符串长度未知,并且缓冲区(例如)已设置为5,则此操作无效。我需要能够为碰巧大于缓冲区的字符串动态分配或重新分配缓冲区的东西。

5 个答案:

答案 0 :(得分:17)

使用snprintf

大多数人会告诉您使用snprintf(),因为它不会超出缓冲区的末尾。

这是好建议。通常的“设计模式”是声明一个临时固定大小的缓冲区,该缓冲区大于字符串可能的大小并且snprintf()。如果字符串需要保存一段时间,则可以测量其长度,malloc(3)另一个,以及strcpy(3)临时缓冲区到半永久malloc()缓冲区。


两遍法

还有另一种方式。

C99指定如果缓冲区为NULL,则不写入任何字节,但返回将写入的实际长度。这允许你做一个虚拟的第一遍,malloc()一个缓冲区,然后snprintf()到那个缓冲区。 (理论上你可以使用普通sprintf(),因为长度现在已知,但我不会。)

无论如何,如果我的程序必须运行在过去曾经制作的每个操作系统上,我不确定我会依赖所有这些,但对于大多数人来说,这些天使用它是合理的模式。

答案 1 :(得分:8)

你想要的是这两个功能之一: * snprintfhttp://libslack.org/manpages/snprintf.3.html)。它将输出缓冲区的长度作为其第二个参数,如果缓冲区对于结果来说太小,它将返回所需的字符数,允许您重新分配更大的缓冲区。 * asprintf。它需要一个char **参数并分配足够的内存来保存输出,只要有足够多的连续虚拟内存可用。如果在程序退出之前已经完成了它,则必须调用free将其从内存中删除,并且可能需要内存用于其他内容。

答案 2 :(得分:2)

根据DigitalRoss的建议,我把它作为一种锻炼方式扔到了一起。如果你有代表(我没有),请随意将其与他的答案合并。

#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char* createRandstr(void);

int main(void)
{
    char* mystr;
    char* randomString;
    size_t len;

    /* Makes a random string of A's */
    randomString = createRandstr();

    /* 1st pass gets needed size */
    len = (size_t)snprintf(NULL, 0, "random string -->%s<--\n", randomString);
    mystr = malloc(len);

    /* Safely write to mystr with known length 'len' */
    snprintf(mystr, len, "random string -->%s<--\n", randomString);
    puts(mystr);

    free(mystr);
    free(randomString);

    return 0;
}

char* createRandstr(void)
{
    char*  randstr;
    size_t randlen;
    unsigned int i;

    srand((unsigned int)time((time_t*)NULL)); /* Seed rand() */
    randlen = (size_t)rand() % 50; /* Limit to max of 49 chars */
    randstr = malloc(randlen);

    for (i=0; i < randlen; i++)
    {
        randstr[i] = 'A';
    }
    randstr[randlen - 1] = '\0';

    return randstr;
}

答案 3 :(得分:1)

首先,使用snprintf代替sprintfsnprintf需要额外的参数,即要写入的最大字节数(包括尾随'\0')。 snprintf然后返回写入的字节数,不包括尾随'\0'。因此,您可以分配一个认为足够大的缓冲区并尝试调用。如果它到达缓冲区的末尾,则释放该缓冲区并创建一个较大的缓冲区并再次尝试。冲洗并重复。

答案 4 :(得分:1)

您可以使用snprintf or asprintf。 snprintf涉及检查大小并在必要时调整大小,而asprintf只是为您分配正确的大小。