正如标题所述,如何在给定未知格式字符串的情况下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;
}
答案 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
,直到找到NULL
(argv[argc]
保证为NULL
)。
此代码使用固定缓冲区大小char buf[MAX_LENGTH];
,然后您可以strdup
创建返回值或其他任何内容。如果您想避免MAX_LENGTH
限制,那么您必须为每个案例拨打snprintf
两次:一次查找所需的长度;然后malloc
,然后再次调用它来进行实际打印。
如果您希望使用较少剪切和粘贴的版本,则需要使用该算法的大纲:
argc
format
。 (如果您正在编写生成格式的代码,则可以只提供令牌列表而不是字符串)snprintf(buf + upto, space_remaining, token, argv[i++]);
执行此操作时,您需要跟踪已用完的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$