如何为 returns 字符串结果的 sprintf 制作包装器?
How to make a wrapper for sprintf that returns the string result?
我希望能够将格式化的字符串传递给其他函数,例如我的错误处理程序:
error_handler(str_format("error code %d in graphics function: %s", code, error));
我的第一个想法是使用 sprintf,但它没有 return 结果,所以你必须添加额外的行:
char msg = [100];
sprintf(msg, "error code %d in graphics function: %s", code, error)
error_handler(msg);
如果错误消息大于 msg 的大小,则可能存在问题。
所以我想我可以做一个像这样的包装函数:
char* str_format (char* format, ...) {
char* output = malloc(100);
va_list args;
va_start(args, format);
vsnprintf(output, 100, format, args);
va_end(args);
return output;
}
但我仍然有不知道输出所需大小的问题。所以我想我可以使用 sizeof 来获取格式的大小和所有 args,但是没有办法检测有多少 args,除非你每次都手动传递一些 args。我可以读取格式字符串并查找“%”符号并检测每个占位符和类型,但这似乎太复杂了,只是为了获取 sprintf 的 return 值。
我知道有一个名为 snprintf 的函数是安全的并且会限制字符数,但是 c
中没有 vsnprintf
EDIT: #1 将输出更改为 malloc #2 毕竟有一个 vsnprintf 函数,所以我改成了那个。那么问题就在于是否有比这种类型的包装函数更简单的方法。谢谢
有没有简单的方法让 sprintf 变成 return 格式化的字符串?
通常,您为此调用两次 vsnprintf
-- 第一次调用没有缓冲区以获得所需的大小,第二次调用填充缓冲区
char* str_format (char* format, ...) {
va_list args;
va_start(args, format);
int len = vsnprintf(0, 0, format, args);
va_end(args);
char *output = malloc(len+1);
va_start(args, format);
vsnprintf(output, len+1, format, args);
va_end(args);
return output;
}
注意调用者需要释放分配的指针(否则会泄漏)
So I thought I could make a wrapping function like this:
/* !!! DON'T DO THIS !!! */
char* str_format (char* format, ...) {
char output[100];
va_list args;
va_start(args, format);
vsprintf(output, format, args);
va_end(args);
return output;
}
这很糟糕,原因有很多:首先,正如您所说,您不知道输出的大小。然而,最重要的是,您不可能 return 本地定义的变量(如 output
)。
要知道输出的大小,您可以首先对 vsnprintf
进行“虚拟”调用,大小为 0
,according to the manual 应该简单地计算并且 return 输出缓冲区所需的大小。
根据 return 一个有效的指针,您可以通过两种不同的方式完成这项工作:
使用 malloc
分配缓冲区,然后 return 指向已分配缓冲区的指针。这可能很烦人,因为您需要始终 free()
分配的缓冲区,并且由于您想像 error_handler(str_format(...))
那样使用函数,因此您失去了对 returned 缓冲区的引用,因此您要么 free()
它在 error_handler()
中并始终假设 error_handler()
将采用 malloc
对象,或者您需要一个临时变量。
使用 static
缓冲区。然而,这有一个问题,即需要在编译时预先分配一个恒定大小。您可以选择此选项并限制错误消息的大小。这种方法的另一个限制是一次只能格式化一个错误(即这不是线程安全的),因为您每次都会覆盖相同的 static
缓冲区。这在 C 库中很常见,因为它无痛且易于实现,但只有在您有合理大小的错误消息时才真正适用。
选项 1(省略错误检查):
char* str_format(char* format, ...) {
char *buf;
int size;
va_list args;
va_start(args, format);
size = vsnprintf(NULL, 0, format, args) + 1;
va_end(args);
buf = malloc(size);
va_start(args, format);
vsnprintf(NULL, size, format, args);
va_end(args);
return buf;
}
选项 2:
char* str_format(char* format, ...) {
static char buf[1024]; // fixed at compile time
va_list args;
va_start(args, format);
vsnprintf(NULL, 1024, format, args);
va_end(args);
return buf;
}
请注意,对于 vsnprintf
,您需要至少定义以下功能测试宏之一,如手册所述:
#define _XOPEN_SOURCE 500
// or
#define _ISOC99_SOURCE
// or
#define _BSD_SOURCE // old glibc <= 2.19
我希望能够将格式化的字符串传递给其他函数,例如我的错误处理程序:
error_handler(str_format("error code %d in graphics function: %s", code, error));
我的第一个想法是使用 sprintf,但它没有 return 结果,所以你必须添加额外的行:
char msg = [100];
sprintf(msg, "error code %d in graphics function: %s", code, error)
error_handler(msg);
如果错误消息大于 msg 的大小,则可能存在问题。
所以我想我可以做一个像这样的包装函数:
char* str_format (char* format, ...) {
char* output = malloc(100);
va_list args;
va_start(args, format);
vsnprintf(output, 100, format, args);
va_end(args);
return output;
}
但我仍然有不知道输出所需大小的问题。所以我想我可以使用 sizeof 来获取格式的大小和所有 args,但是没有办法检测有多少 args,除非你每次都手动传递一些 args。我可以读取格式字符串并查找“%”符号并检测每个占位符和类型,但这似乎太复杂了,只是为了获取 sprintf 的 return 值。
我知道有一个名为 snprintf 的函数是安全的并且会限制字符数,但是 c
中没有 vsnprintfEDIT: #1 将输出更改为 malloc #2 毕竟有一个 vsnprintf 函数,所以我改成了那个。那么问题就在于是否有比这种类型的包装函数更简单的方法。谢谢
有没有简单的方法让 sprintf 变成 return 格式化的字符串?
通常,您为此调用两次 vsnprintf
-- 第一次调用没有缓冲区以获得所需的大小,第二次调用填充缓冲区
char* str_format (char* format, ...) {
va_list args;
va_start(args, format);
int len = vsnprintf(0, 0, format, args);
va_end(args);
char *output = malloc(len+1);
va_start(args, format);
vsnprintf(output, len+1, format, args);
va_end(args);
return output;
}
注意调用者需要释放分配的指针(否则会泄漏)
So I thought I could make a wrapping function like this:
/* !!! DON'T DO THIS !!! */ char* str_format (char* format, ...) { char output[100]; va_list args; va_start(args, format); vsprintf(output, format, args); va_end(args); return output; }
这很糟糕,原因有很多:首先,正如您所说,您不知道输出的大小。然而,最重要的是,您不可能 return 本地定义的变量(如 output
)。
要知道输出的大小,您可以首先对 vsnprintf
进行“虚拟”调用,大小为 0
,according to the manual 应该简单地计算并且 return 输出缓冲区所需的大小。
根据 return 一个有效的指针,您可以通过两种不同的方式完成这项工作:
使用
malloc
分配缓冲区,然后 return 指向已分配缓冲区的指针。这可能很烦人,因为您需要始终free()
分配的缓冲区,并且由于您想像error_handler(str_format(...))
那样使用函数,因此您失去了对 returned 缓冲区的引用,因此您要么free()
它在error_handler()
中并始终假设error_handler()
将采用malloc
对象,或者您需要一个临时变量。使用
static
缓冲区。然而,这有一个问题,即需要在编译时预先分配一个恒定大小。您可以选择此选项并限制错误消息的大小。这种方法的另一个限制是一次只能格式化一个错误(即这不是线程安全的),因为您每次都会覆盖相同的static
缓冲区。这在 C 库中很常见,因为它无痛且易于实现,但只有在您有合理大小的错误消息时才真正适用。
选项 1(省略错误检查):
char* str_format(char* format, ...) {
char *buf;
int size;
va_list args;
va_start(args, format);
size = vsnprintf(NULL, 0, format, args) + 1;
va_end(args);
buf = malloc(size);
va_start(args, format);
vsnprintf(NULL, size, format, args);
va_end(args);
return buf;
}
选项 2:
char* str_format(char* format, ...) {
static char buf[1024]; // fixed at compile time
va_list args;
va_start(args, format);
vsnprintf(NULL, 1024, format, args);
va_end(args);
return buf;
}
请注意,对于 vsnprintf
,您需要至少定义以下功能测试宏之一,如手册所述:
#define _XOPEN_SOURCE 500
// or
#define _ISOC99_SOURCE
// or
#define _BSD_SOURCE // old glibc <= 2.19