What is the equivalent of PHP vsprintf() in C?
I've this PHP code and want the equivalent in C for performance reasons. I've done R&D Google; I didn't get any solution.
The code is:
<?php
$array = array('tom','jerry','cat'); //variable
$tmpl = 'test %s for %s with %s'; //args taken from $array
print vsprintf($tmpl,$ar开发者_如何学JAVAray)."\n";
?>
If you know the string in advance, you could just manually unroll the array:
char *array[] = {"tom", "jerry", "cat"};
char *tmpl = "test %s for %s with %s";
sprintf(out, tmpl, array[0], array[1], array[2])
To do this in C dynamically:
int my_sprintf(char * restrict out, const char * restrict format, int len, char **args){
switch(len) {
case 0: return sprintf(out, format);
case 1: return sprintf(out, format, args[0]);
case 2: return sprintf(out, format, args[0], args[1]);
case 3: return sprintf(out, format, args[0], args[1], args[2]);
/* ... add more cases as necessary ... */
}
return -1;
}
You probably could dig into the GCC internals but it won't be portable.
PHP script to generate the relevant code (this should be run before you compile):
int my_sprintf(char * restrict out, const char * restrict format, int len, char **args){
switch(len) {
<?php
for($i=0, $out="";$i<=100;++$i) {
?>
case <?= $i ?>: return sprintf(out, format <?= $out ?>);
<?php
$out .= ", args[$i]";
}
?>
}
}
If you know how many arguments you're passing, you just call snprintf(3)
. If you don't know, the only other option is to use a variadic function and call vsnprintf
. This is commonly used in logging code as a thin wrapper around snprintf
. For example:
void my_log_function(int level, const char *format, ...)
{
if(level >= MIN_LOGGING_LEVEL)
{
va_list ap;
va_start(ap, format);
char buffer[4096];
vsnprintf(buffer, sizeof(buffer), format, ap);
// Now write buffer to a file/stdout/the debugger/etc.
va_end(ap);
}
}
...
my_log_function(LOG_LEVEL_DEBUG, "foo %d bar %s baz", 42, "quux");
If you really only have the arguments in an array, there's no way to do what you want using standard C. You can use libraries such as ffcall, but it's not portable to all systems. For example, here's how you could do it with ffcall:
int array_vsnprintf(char *str, size_t size, const char *format, int *args,
int numargs)
{
av_alist alist; // will hold the argument list
int retval; // will hold the return value from vsnprintf
av_start_int(alist, &vsnprintf, &retval);
// Add the arguments to the argument list. This assumes all of the
// arguments are ints -- if you have heterogeneous types, you need to keep
// track of the type information somewhere and use the appropriate macro
// for each argument.
av_ptr(str);
av_int(size);
av_ptr(format);
int i;
for(i = 0; i < numargs; i++)
av_int(alist, args[i]);
// Now call vsnprintf
av_call(alist);
return retval;
}
So you want some printf-variant that takes the arguments to be printed from an array? You can't do that in C.
There isn't a direct analogue to the PHP variant of vsprintf()
in C. There is a function vsprintf()
- which should not normally be used; it is safer to use vsnprintf()
instead. But that does not take an array of character pointers, nor does any other variant of printf()
.
You have a couple of options. The one outlined by @Foo Bah is one way of doing it; the complicating factor is that you have to write out 30-70 variants, which is ugly, if nothing else.
I think in the circumstances, I would write a variant that parsed the format string and handled the array. It would copy the literal components of the format to the result, and then invoke either snprintf()
to format (copy) elements from the array when requested. I note that formats like %*.*s
are not an option in the context; there is no way to get the integer values passed (or, no clean way to do so). And there is no easy way to support interleaved format specifiers such as %f
and %d
. One interesting question in interface design is 'do you pass the length of the array and validate the length and the format, or do you let the format determine the length of the array'?
int vsnprintfas(char *buffer, size_t buflen, const char *format,
size_t arrlen, char * const * const array);
Or:
int vsnprintfas(char *buffer, size_t buflen, const char *format,
char * const * const array);
Since there's room for mistakes, I'd use the double-checking interface - the first one listed. The suggested as
suffix is for array of strings, of course. I'm quite willing to negotiate on the number and location of the const
qualifiers on array
, but conceptually, it is a set of pointers which the function is not going to change at all.
I've tried this alternative working fine, like get attrs as '|' separated string and patch can i further optimize this if possible, any bottlenecks with this.
char *tmpl = "test %s for %s with %s" , buffer[10000], *attrs="Tom|Jerry|Cat";
tmplpatch(buffer,tmpl,attrs);
printf("%s\n",buffer);
void tmplpatch(char *str, const char *fmt, const char *attrs) {
for(;*str=*fmt, *fmt;++fmt,++str) {
if(*fmt == '\\') {
fmt++;*str=*fmt;continue;
}
if(!(*fmt == '%' && *(fmt+1)=='s')) continue; // ! %s
for(;*attrs!='\0' && *attrs != '|';*str++=*attrs++);
++fmt;++attrs;str--; // skip s, |, junk not '\0'
}
}
精彩评论