【问题标题】:How to prepend formatted data to an printf() call如何将格式化数据添加到 printf() 调用
【发布时间】:2020-02-15 22:44:45
【问题描述】:

我想实现一个类似于 printf 的可变参数函数,除了它打印一些前缀。例如,假设我希望前缀是time(0) 的值。如果我打电话:

wrapped_printf("Hello, world %d", 5678);

我会期待这样的:

1571441246 Hello, world 5678

作为输出。

显然,替换格式字符串并不是什么大问题;给我带来麻烦的是可变参数业务。我应该将其实现为采用... 的函数吗?采取va_list?以及如何添加我的额外参数?

这就是我现在所拥有的。它可以编译并运行(它甚至是有效的 C89...),但是额外的参数会被弄乱。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

int wrapped_printf(const char* format_str, ...)
{
        static const char* const prefix = "%d ";
        static const size_t prefix_length = 3;

        va_list ap;
        size_t format_string_length = strlen(format_str);
        char* const prefixed_format_str = malloc(format_string_length + prefix_length + 2);
                /* 1 for the trailing '\0' and 1 for a line break */
        if (prefixed_format_str == NULL) { exit(EXIT_FAILURE); }
        strncpy(prefixed_format_str, prefix, prefix_length);
        strncpy(prefixed_format_str + prefix_length, format_str, format_string_length);
        prefixed_format_str[prefix_length + format_string_length] = '\n';
        prefixed_format_str[prefix_length + format_string_length + 1] = '\0';
        va_start(ap, format_str);
        return printf(
                prefixed_format_str,
                (int) time(0),
                ap);

        va_end(ap);
}

int main()
{
    wrapped_printf("Hello world %d\n", 5678);
    return EXIT_SUCCESS;
}

在 Coliru 上查看 failing

注意事项:

  • 只能一个调用 - 但它可以是printf()vprintf()
  • 可以使用一个大字符串缓冲区,将sprintf()前缀放入其中,然后sprintf()将原始参数放在后面;但这也不是我的意思。
  • 我不介意使用特定于编译器的代码 - 但如果您建议这样做,请尝试在多个平台上覆盖多个编译器 - 特别是 GCC 或带有 AMD64 处理器的 GNU/Linux 上的 clang。

【问题讨论】:

    标签: c printf variadic-functions


    【解决方案1】:

    完全不可能在纯 C 中以可移植的方式将参数添加va_list。这是肯定可以做到的,但是对于每个架构 和 编译器调用约定

    对于一种可移植的方式,像libffi 这样的库和格式字符串的解析器......


    如果您有幸使用 C99+,我建议您尽可能选择macro instead;然后您可以使用编译时字符串连接将"%d " 添加到格式字符串中,并在参数中轻松添加数字。但是格式需要是字符串文字。

    或者如果你真的需要使用一个函数,那么我没有得到不单独使用printf作为前缀的限制,然后是这些 - 输出将是行缓冲或完全缓冲的,所以有不会有什么不同。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    #include <time.h>
    
    #define wrapped_printf_macro(f_, ...) \
        printf("%lld " f_, (long long)time(0), __VA_ARGS__)
    
    int wrapped_printf(const char* format_str, ...)
    {
        static const char* const prefix = "%d ";
        static const size_t prefix_length = 3;
        va_list ap;
    
        printf("%lld ", (long long int)time(0));
        va_start(ap, format_str);
        vprintf(format_str, ap);
        va_end(ap);
    }
    
    
    int main()
    {
        wrapped_printf_macro("Hello world %d\n", 5678);
        wrapped_printf("Hello world %d\n", 5678);
        return EXIT_SUCCESS;
    }
    

    【讨论】:

    • @einpoklum mine 有 2 个选项。一个宏一个包装器。但当然你可以为所欲为。
    • 是的,你说得对,我猜我有自己的想法 :-) ...请注意,您的第二个选项违反了我的要求,即只发出一个 printf()...跨度>
    • 你的要求还是挺奇怪的。真正的原因是什么?请问怎么做?我已经告诉过 - 便携几乎是不可能的
    • 真正的原因是我在一个环境中工作,它有一个类似 printf 的函数,它不是 printf 并且做一些硬件特定的事情,我不能调用它两次,一次.
    猜你喜欢
    • 1970-01-01
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 2011-04-09
    • 1970-01-01
    • 2022-01-22
    • 2013-04-17
    相关资源
    最近更新 更多