【问题标题】:building a string out of a variable amount of arguments用可变数量的参数构建一个字符串
【发布时间】:2011-09-17 05:31:41
【问题描述】:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>


int main(int argc, char * argv[])
{
    char *arr[] = { "ab", "cd", "ef" };
    char **ptr, **p, *str;
    int num = 3;
    int size = 0;

    ptr = calloc(num, 4);
    p = ptr;

    for (; num > 0; num--)
            size += strlen(*(p++) = arr[num - 1]);

    str = calloc(1, ++size);
    sprintf(str, "%s%s%s", ptr[0], ptr[1], ptr[2]);

    printf("%s\n", str);

    return 0;
}

输出:"efcdab" 符合预期。

现在,如果sprintf 的参数计数是预先确定的并且是已知的,那么这一切都很好并且合适。但是,如果参数计数是可变的 (ptr[any]),我想要实现的是一种构建字符串的优雅方式。

第一个问题:需要传递给sprintf 的第二个参数是const char *format
第二:第三个参数是实际传递的参数数量,以便根据提供的format 构建字符串。

我怎样才能实现以下目标:

sprintf(str, "...", ...)

基本上,如果函数接收到 4 个(或更多)char 指针,我想从中构建一个完整的字符串(目前,在上面提供的代码中,只有 3 个)。这意味着,第二个参数必须(至少)采用"%s%s%s%s" 的形式,后跟ptr[0], ptr[1], ptr[2], ptr[3] 的参数列表。

首先如何才能对sprintf(或vsprintf)进行这样的“组合”调用?事情会更容易,如果我可以提供一个完整的指针数组(**ptr)作为第三个参数,而不是..但这似乎不可行?至少,不是sprintf 能理解的方式,所以看起来.. 因为它需要某种特殊形式的format

想法/建议?

【问题讨论】:

  • 主要问题似乎是 sprintf 没有做你想做的事。你听说过strcat吗?
  • @karlphillip:这就是我提到“优雅”的原因,字符串复制解决方案依赖于缓冲区操作。问这个问题的全部目的是逃避这种行为并利用某种“分组”。
  • 我确实有答案...切换到 C++ 并使用 std::string!
  • 但是从技术上讲,您在这里所做的也是缓冲区操作,不是吗?您的 str 变量在此处充当目标缓冲区。
  • @JAB:我们不要陷入语义争议。是的,当然是。我想你已经完全理解我的意思了。如果您有更好的term 来实现我指定的目标 - 我很乐意听到。

标签: c arrays string pointers printf


【解决方案1】:

karlphillip 对strcat 的建议似乎确实是这里的解决方案。或者更确切地说,您更可能想要使用 strncat 之类的东西(尽管如果您正在使用支持它的 C 库,我会推荐 strlcat,在我看来,它比 @ 好得多987654324@).

所以,除了sprintf(str, "%s%s%s", ptr[0], ptr[1], ptr[2]);,你可以这样做:

int i;

for (i = 0; i < any; i++)
    strncat(str, arr[i], size - strlen(str) - 1);

(或strlcat(str, arr[i], size);strlcat 的好处在于,如果目标缓冲区太小,它的返回值将指示重新分配需要多少字节,但这不是标准的 C 函数和很多系统不支持。)

【讨论】:

  • 一半的string.h 可以在这里解决。但是,我不确定这会有多少价值。因为我故意无视这种方法,希望有更好的方法。我想,不过,我可以用strcat 自己管理。如果出现问题,请告诉我,您会找到更好的方法;)
  • 您始终可以创建自己的函数库来支持分段字符串并忽略实际连接。但在我看来,这至少与切换到 C++ 一样过分,如果不是更过分的话。
【解决方案2】:

C 中没有其他方法可以在不操作缓冲区的情况下做到这一点。

但是,您可以切换到 C++ 并使用出色的 std::string 让您的生活更轻松。

【讨论】:

  • 或者人们可以避免添加毫无意义和无用的 cmets,就像切换到其他东西一样重要。但是,感谢您评论的另一部分,如果这是真的并且真的不可能的话。
【解决方案3】:

您的第一个问题由以下人员处理:const char * 是针对函数的,而不是针对您的。把你自己的字符串放在一起——那个签名只是意味着函数不会改变它。

您的第二个问题通过以下方式处理:传入您自己的 va_list。你怎么得到它?制作你自己的可变参数函数:

char *assemble_strings(int count, ...)
{
    va_list data_list;
    va_list len_list;
    int size;
    char *arg;
    char *formatstr;
    char *str;
    int i;

    va_start(len_list, count);
    for (i = 0, size = 0; i < count; i++)
    {
        arg = va_arg(len_list, char *);
        size += strlen(arg);
    }
    va_end(len_list);

    formatstr = malloc(2*count + 1);
    formatstr[2*count] = 0;
    for (i = 0; i < count; i++)
    {
        formatstr[2*i] = '%';
        formatstr[2*i+1] = 's';
    }
    str = malloc(size + 1);

    va_start(data_list, count);
    vsprintf(str, formatstr, data_list);
    va_end(data_list);

    free(formatstr);

    return(str);
}

当然,您需要一些方法来终止可变参数,如果字符串列表完全在可变参数中,将它传递给 vsprintf 会容易得多——因为标准 C 至少需要一个常规参数。

【讨论】:

    【解决方案4】:

    我将用于最终复制到 str 中的循环类似于:

    for(i=0, p=str; i < num; i++)
        p += sprintf(p, "%s", ptr[i]);
    

    for(i=0, p=str; i < num; i++)
        p += strlen(strcpy(p, ptr[i]));
    

    而不是试图在对 sprintf 的一次调用中处理可变数量的参数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-12-19
      • 2017-08-24
      • 1970-01-01
      • 2020-01-09
      • 2013-07-17
      • 1970-01-01
      • 2011-09-02
      相关资源
      最近更新 更多