【问题标题】:Creating a format string with a variable number of specifiers创建具有可变数量的说明符的格式字符串
【发布时间】:2020-11-22 19:39:49
【问题描述】:

我正在尝试使用 va_list 及其关联的宏与 vsprintf() 来创建具有可变数量的说明符的格式字符串。这是我编写的示例程序,其中说明符的数量只能通过 NUM_ARG 宏来更改:

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

#define MAXBUF      4096
#define SPECIFIER   "(%s)"
#define NUM_ARG     5

char *strmaker(int num_args, ...)
{
    char form[MAXBUF] = { [0] = '\0' };
    char *prnt = (char *) malloc(sizeof(char) * MAXBUF);
    va_list strings;
    
    for (int i = 0; i < num_args; ++i)
        strcat(form, SPECIFIER);
    
    va_start(strings, num_args);    
    vsprintf(prnt, form, strings);
    va_end(strings);

    return prnt;
}

int main(int argc, char *argv[])
{
    if (argc != (NUM_ARG + 1))
        return -1;

    char *s = strmaker(NUM_ARG, argv[1], argv[2], argv[3], argv[4], argv[5]);   
    printf("%s\n", s);
    free(s);

    return 0;
}

但是,这并不是我想要实现的。我怎么能用可变数量的参数来做到这一点?如何将可变数量的字符串传递给函数并用于初始化 va_list?

【问题讨论】:

    标签: c formatting printf variadic-functions


    【解决方案1】:

    据我所知,这是不可能的。如果你不是那么热衷于使用可变参数函数,可以重新定义函数。以下代码适合您的需要;遍历数组中的每一项并使用snprintf 附加到字符串。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAXBUF      4096
    #define SPECIFIER   "(%s)"
    
    char *strmaker(int num_args, char** strings)
    {
        char *prnt = (char *) malloc(sizeof(char) * MAXBUF);
        int cur = 0;
    
        /* Append the strings to the prnt buffer */
    
        for (int i = 0; i < num_args; i++) {
            int p_return = snprintf(prnt + cur, MAXBUF - cur, SPECIFIER, strings[i]);   // If no error, return the number characters printed excluding nul (man page)
    
            if (p_return >= MAXBUF - cur)   // If buffer overflows (man page)
                return prnt;
    
            cur = cur + p_return;   // Update the index location.
        }
    
        return prnt;
    }
    
    int main(int argc, char *argv[])
    {
        if (argc <= 1)
           return -1;
    
        char *s = strmaker(argc - 1, argv + 1);
        printf("%s\n", s);
        free(s);
    
        return 0;
    }
    

    终端会话:

    $ ./a.out 1 2 3 
    (1)(2)(3)
    $ ./a.out 1 2 3 4 5 6 7
    (1)(2)(3)(4)(5)(6)(7)
    $ ./a.out Hello, This is stackoverflow, Bye 
    (Hello,)(This)(is)(stackoverflow,)(Bye)
    

    【讨论】:

    • 嗯,这可以与已经创建的格式字符串一起使用吗? IE。想象一个格式字符串,例如“he(%s)llo(%s)”(包含说明符和文本)。我附加了一个变量“count”来跟踪说明符。然后如何使用 printf 函数之一将正确数量的字符串打印到格式字符串?
    • @GabrielSaul 您的问题已经得到解答。如果您有新问题,请创建一个新问题,而不是像这样在评论中更新约束。谢谢。
    【解决方案2】:

    简短的回答是:你不能。

    但是,您可以通过使用可能是动态分配的字符串数组来解决它。然后,您基本上可以使用与现在相同的技术,但改为遍历数组。


    大概是这样的:

    char *strmaker(size_t count, char *strings[])
    {
        // First get the length of all strings in the array
        size_t result_length = 0;
    
        for (size_t i = 0; i < count; ++i)
        {
            // +1 for space between the strings
            // And for the last string adds space for the string null-terminator
            result_length += strlen(strings[i]) + 1;
        }
    
        // Now allocate the string (using calloc to initialize memory to zero, same as the string null-terminator)
        char *result = calloc(1, result_length);
    
        // And not concatenate all strings in the array into one large string
        for (size_t i = 0; i < count; ++i)
        {
            strcat(result, strings[i]);
    
            if (i != count - 1)
            {
                strcat(result, " ");  // Add space, except after last string
            }
        }
    
        // Return the resulting string
        return string;
    }
    
    int main(int argc, char *argv[])
    {
        // Create an array for all arguments
        char **arguments = malloc(sizeof(char *) * argc - 1);
        for (int a = 1; a < argc)
        {
            arguments[a - 1] = argv[a];
        }
    
        // Now create the single string
        char *result = strmaker(argc - 1, arguments);
    
        // ... and print it
        printf("%s\n", result);
    
        // Finally clean up after us
        free(result);
        free(arguments);
    }
    

    对于argv 中的命令行参数,您实际上并不需要创建一个新数组来保存它们,但它展示了如何创建一个字符串数组以传递给strmaker。你可以使用任何你想要的字符串来代替命令行参数。

    【讨论】:

    • 那么,有一个字符串数组,迭代它并为每次迭代调用函数?因此,如果我一次向格式字符串添加一个字符串,是否必须静态分配格式字符串和打印的字符串?
    • @GabrielSaul 不是我的意思。添加示例来回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    • 2011-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-04
    相关资源
    最近更新 更多