簡體   English   中英

如何在給定未知格式字符串的情況下sprintf數組?

[英]How do I sprintf an array given a unknown format string?

正如標題所述,如何在給定未知格式字符串的情況下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;
}

(這個答案考慮了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. 令牌format (如果您正在編寫生成格式的代碼,您可以只提供令牌列表而不是字符串)
  3. 對於每個令牌,調用snprintf(buf + upto, space_remaining, token, argv[i++]);

雖然這樣做,你需要跟蹤多少的buf你已經使用了(和realloc每一步吧,如果你打算使用雙snprintf方法)。 在到達argc之前檢查你是否停止。

今晚太多時間在我的手上,所以這里有一個簡單的 - 如果不是全部那么特別有效 - 的方式。 當你說“我只打算使用修飾符這樣的%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$ 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM