Что эквивалентно PHP vsprintf () в C? - PullRequest
1 голос
/ 03 сентября 2011

У меня есть этот PHP-код, и я хочу получить эквивалент в C по соображениям производительности.Я сделал R & D Google;Я не получил никакого решения.

Код:

<?php
   $array = array('tom','jerry','cat'); //variable 
   $tmpl  = 'test %s for %s with %s'; //args taken from $array
   print vsprintf($tmpl,$array)."\n";
?>

Ответы [ 5 ]

1 голос
/ 03 сентября 2011

Если вы знаете, сколько аргументов вы передаете, просто позвоните snprintf(3). Если вы не знаете, единственный другой вариант - использовать функцию с переменным числом аргументов и вызвать vsnprintf. Это обычно используется при регистрации кода в качестве тонкой оболочки вокруг snprintf. Например:

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");

Если у вас на самом деле есть только аргументы в массиве, нет способа сделать то, что вы хотите, используя стандартный C. Вы можете использовать библиотеки, такие как ffcall , но это не переносимо для всех систем. Например, вот как вы можете сделать это с помощью 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;
}
1 голос
/ 03 сентября 2011

Если вы знаете строку заранее, вы можете просто развернуть массив вручную:

char *array[] = {"tom", "jerry", "cat"};
char *tmpl = "test %s for %s with %s";
sprintf(out, tmpl, array[0], array[1], array[2])

Чтобы сделать это в C динамически:

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;
}

Возможно, вы могли бы покопаться во внутренних органах GCC, но он не будет переносимым.

PHP-скрипт для генерации соответствующего кода (его нужно запустить перед компиляцией):

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]"; 
            } 
        ?>
    }
}
0 голосов
/ 24 сентября 2011

Я попробовал эту альтернативу, которая работает нормально, например, получаю attrs как '|' разделенная строка и патч Могу ли я дополнительно оптимизировать это, если это возможно, любые узкие места с этим.

 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'
  }
}
0 голосов
/ 03 сентября 2011

Нет прямого аналога варианта PHP vsprintf() в C. Есть функция vsprintf() - которая обычно не должна использоваться;вместо этого безопаснее использовать vsnprintf().Но для этого не требуется массив указателей символов и любой другой вариант printf().

. У вас есть несколько вариантов.Один из описанных @ Foo Bah является одним из способов сделать это;усложняющим фактором является то, что вы должны выписать 30-70 вариантов, что уродливо, если не сказать больше.

Я думаю, что в этих обстоятельствах я бы написал вариант, который проанализировал бы строку формата и обработал массив.Он скопирует литеральные компоненты формата в результат, а затем вызовет либо snprintf() для форматирования (копирования) элементов из массива по запросу.Я отмечаю, что форматы типа %*.*s не являются опцией в контексте;нет способа получить переданные целочисленные значения (или нет clean способа сделать это).И нет простого способа поддержки чередующихся спецификаторов формата, таких как %f и %d.Один интересный вопрос в дизайне интерфейса: «Вы передаете длину массива и проверяете длину и формат, или вы позволяете формату определять длину массива»?

int vsnprintfas(char *buffer, size_t buflen, const char *format,
                size_t arrlen, char * const * const array);

Или:

int vsnprintfas(char *buffer, size_t buflen, const char *format,
                char * const * const array);

Поскольку есть место для ошибок, я бы использовал интерфейс двойной проверки - первый в списке.Предложенный суффикс as предназначен для массива строк , конечно.Я вполне готов договориться о количестве и расположении квалификаторов const на array, но концептуально это набор указателей, функция которых вообще не изменится.

0 голосов
/ 03 сентября 2011

Итак, вы хотите какой-нибудь printf-вариант, который принимает аргументы для печати из массива? Вы не можете сделать это в C.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...