如何在给定未知格式字符串的情况下sprintf数组?

时间:2014-05-04 02:56:21

标签: c

正如标题所述,如何在给定未知格式字符串的情况下sprintf数组?即如何填写my_sprintf

char* my_sprintf(const char *format, char **args){
    char *result = malloc(MAX_SIZE_STRING*sizeof(char));
    // ???
    return result;
}

int main(int argc, char *argv[]){
    printf("%s\n", my_sprintf("%s %s %s", argv));
    return 0;
}

2 个答案:

答案 0 :(得分:1)

(这个答案考虑了OP的评论“我只想用修饰符来表示%s”)

请注意,可能可以构建您自己的va_list,但是这个细节是特定于实现的并且非常不便携,所以我不打算这样做路。

这可以说是最简单的方法:

if ( argc == 1 ) snprintf(buf, sizeof buf, format, argv[0]);
else if ( argc == 2 ) snprintf(buf, sizeof buf, format, argv[0], argv[1]);
else if ( argc == 3 ) snprintf(buf, sizeof buf, format, argv[0], argv[1], argv[2]);
// ...etc, so far as you want

通过循环argc来找出argv,直到找到NULLargv[argc]保证为NULL)。

此代码使用固定缓冲区大小char buf[MAX_LENGTH];,然后您可以strdup创建返回值或其他任何内容。如果您想避免MAX_LENGTH限制,那么您必须为每个案例拨打snprintf两次:一次查找所需的长度;然后malloc,然后再次调用它来进行实际打印。

如果您希望使用较少剪切和粘贴的版本,则需要使用该算法的大纲:

  1. 查找argc
  2. Tokenize format。 (如果您正在编写生成格式的代码,则可以只提供令牌列表而不是字符串)
  3. 对于每个令牌,请致电snprintf(buf + upto, space_remaining, token, argv[i++]);
  4. 执行此操作时,您需要跟踪已用完的buf的多少(如果您要使用双倍realloc,则需要snprintf。{{} 1}}方法)。另请检查您是否在到达argc之前停止。

答案 1 :(得分:1)

今晚我的手太多了,所以这里有一个简单的 - 如果不是全部那么特别有效 - 的方式。当你说“我只打算%s使用修饰符等”时,我会直截了当地说你的话,特别是这不会处理%%等等:

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

const static size_t initial_list_size = 20;


/*
 * Returns a list of indices of `format` at which the substring
 * "%s" is located. -1 is the sentinel value for end of list.
 * Caller is responsible for freeing the returned pointer.
 */

int * find_specifiers(const char * format) {
    size_t list_size = initial_list_size, top = 0;

    /*  Dynamically allocate list for locations of specifiers  */

    int * loc_list = malloc(list_size * sizeof *loc_list);
    if ( loc_list == NULL ) {
        fputs("Error allocating memory.", stderr);
        exit(EXIT_FAILURE);
    }
    loc_list[top] = -1;

    /*  Find all occurrences  */

    const char * needle = format;
    while ( (needle = strstr(needle, "%s")) != NULL ) {

        /*  Add index of found substring to list  */

        loc_list[top++] = needle - format;

        /*  Increase size of list if necessary  */

        if ( top >= list_size ) {
            list_size *= 2;
            loc_list = realloc(loc_list, list_size * sizeof *loc_list);
            if ( loc_list == NULL ) {
                fputs("Error allocating memory.", stderr);
                exit(EXIT_FAILURE);
            }
        }

        /*  Set new sentinel value and skip past current specifier  */

        loc_list[top] = -1;
        needle += 2;
    }

    return loc_list;
}


/*
 * Returns a dynamically allocated string equivalent to `format`
 * with each occurrence in `format` of "%s" replaced with successive
 * strings in the array pointed to by `args`. Caller is responsible
 * for freeing the returned pointer.
 */

char * my_sprintf(const char *format, char **args){
    int * loc_list = find_specifiers(format);
    size_t outsize = strlen(format) + 1;

    /*  Calculate required size of output string  */

    for ( size_t i = 0; loc_list[i] != -1; ++i ) {
        outsize += strlen(args[i]) - 2;
    }

    /*  Allocate output string with calloc() to avoid
     *  the need to manually null-terminate.           */

    char *result = calloc(1, outsize);
    if ( result == NULL ) {
        fputs("Error allocating memory.", stderr);
        exit(EXIT_FAILURE);
    }

    /*  Copy `format`, and replace specifiers with
     *  successive strings contained in `args`      */

    size_t n_out = 0, current_spec = 0, n_fmt = 0;
    while ( format[n_fmt] ) {

        /*  Copy the next argument  */

        if ( loc_list[current_spec] != -1 &&
            n_fmt == (size_t) loc_list[current_spec] ) {
            size_t n_arg = 0;
            while ( args[current_spec][n_arg] ) {
                result[n_out++] = args[current_spec][n_arg++];
            }
            ++current_spec;
            n_fmt += 2;
        }
        else {

            /*  Copy the next character of `format`  */

            result[n_out++] = format[n_fmt++];
        }
    }

    free(loc_list);
    return result;
}

int main(void){
    char * args[] = {"These", "are", "args"};
    char * result1 = my_sprintf("Arg 1: %s, Arg 2: %s, Arg 3 %s", args);
    char * result2 = my_sprintf("There are no args here.", NULL);

    printf("%s\n", result1);
    printf("%s\n", result2);

    free(result1);
    free(result2);

    return 0;
}

输出:

paul@MacBook:~/Documents/src/scratch$ ./mysprint
Arg 1: These, Arg 2: are, Arg 3 args
There are no args here.
paul@MacBook:~/Documents/src/scratch$