【问题标题】:writing formatted data of unknown length to a string (C programming)将未知长度的格式化数据写入字符串(C 编程)
【发布时间】:2023-03-17 01:35:01
【问题描述】:

以下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),这将不起作用。我需要一些可以为碰巧大于缓冲区的字符串动态分配或重新分配缓冲区的东西。

【问题讨论】:

  • @Charles:这是作业吗?
  • @RageZ:不,不是。我只是一个想在空闲时间学习一些 C 的初学者。
  • @RageZ。哈哈哈

标签: c string printf


【解决方案1】:

使用 snprintf

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

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


两遍方法

还有另一种方法。

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

无论如何,如果我的程序必须在过去制作的所有操作系统上运行,我不确定我是否会指望这一切,但对于当今大多数人来说,这是一个合理的模式。

【讨论】:

    【解决方案2】:

    您想要的是以下两个功能之一:

    • snprintf (http://libslack.org/manpages/snprintf.3.html)。它将输出缓冲区的长度作为其第二个参数,如果缓冲区对于结果来说太小,它将返回所需的字符数,从而允许您重新分配更大的缓冲区。

    • asprintf。它需要一个char ** 参数并分配足够的内存来保存输出,只要有那么多连续的虚拟内存可用。如果您在程序退出之前完成了它,您必须调用free 将其从内存中删除,并且可能需要内存来做其他事情。

    【讨论】:

    • 摇滚吧!我得去看看
    【解决方案3】:

    根据 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;
    }
    

    【讨论】:

    • 您应该将len 保留为int 并添加检查len&gt;=0
    • 另外,你需要一个len+1的缓冲区
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 鲁滨逊有危险! asprintf 是 GNU 扩展,不是 C 或 POSIX 的一部分
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多