我需要将某些内容转储到文本文件中,同样需要在屏幕上显示。 (我说的是C程序的实用程序) 菜单选项如下所示,
1. display AA parameters 2. display BB parameters 3. display CC parameters 4. dump all 5. Exit Select option >
如果他们选择1/2/3,它只需要在屏幕上显示,或者如果他们选择选项#4,则需要逐个显示所有参数,同样需要转储到.txt文件中。< / p>
我知道,我们可以使用printf和fprintf函数在屏幕上显示并分别将其写入文本文件。问题是我显示了20多个参数,每个参数至少有20个子参数。
我目前的实施方式如下,
printf ( "Starting serial number [%ld]\n",
serial_info_p->start_int_idx);
fprintf(file_p, "Starting serial number [%ld]\n",
serial_info_p->start_int_idx)
printf ( "Current Serial number [%d]\n",
serial_info_p->current_int_idx);
fprintf(file_p, "Current Serial number [%d]\n",
serial_info_p->current_int_idx);
是否有最简单的方法来实现此目的以减少代码行数?
答案 0 :(得分:7)
编辑: C ++标签似乎有误导性,有人可以将其删除吗?谢谢:)
我使用可变参数宏来自定义printf和朋友。
我会写这样的东西:
#define tee(fp,fmt, ...) \
{ \
printf (fmt, __VA_ARGS__); \
fprintf (fp, fmt, __VA_ARGS__); \
}
(名称来自tee(1)实用程序)
答案 1 :(得分:3)
这样的东西允许你添加任意数量的输出流,并且只需修改PrintTarget链表就可以在运行时更改它们。
/** gcc -Wall -o print_target print_target.c && ./print_target */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct PrintTarget* PrintTargetp;
void* xmalloc (size_t size);
PrintTargetp pntCreate (PrintTargetp head, FILE* target);
void pntDestroy (PrintTargetp list);
typedef struct PrintTarget
{
FILE* target;
PrintTargetp next;
} PrintTarget;
void myPrintf (PrintTargetp streams, char* format, ...)
{
va_list args;
va_start(args, format);
while (streams)
{
vfprintf(streams->target, format, args);
streams = streams->next;
}
va_end(args);
}
int main(void)
{
PrintTargetp streams = pntCreate(NULL, stdout);
streams = pntCreate(streams, fopen("somefile.txt", "a+")); //XXX IO errors?
myPrintf(streams, "blah blah blah...\n");
pntDestroy(streams);
return 0;
}
以下是辅助功能的定义:
PrintTargetp pntCreate (PrintTargetp head, FILE* target)
{
PrintTargetp node = xmalloc(sizeof(PrintTarget));
node->target = target;
node->next = head;
return node;
}
void pntDestroy (PrintTargetp list)
{
while (list)
{
PrintTargetp next = list->next;
free(list);
list = next;
//XXX cycles?
//XXX close files?
}
}
void* xmalloc (size_t size)
{
void* p = malloc(size);
if (p == NULL)
{
fputs("malloc error\n", stderr);
abort();
}
return p;
}
答案 2 :(得分:2)
您也可以将程序的输出传递给tee(1)
命令。
答案 3 :(得分:1)
如果您正在编写控制台应用程序,您应该能够使用以下内容输出到屏幕(标准输出):
fprintf(stdout, "Hello World\n");
这使您可以将打印数据的代码移动到自己的函数,并传入FILE *进行打印。然后,如果传递“stdout”,该函数可以打印到屏幕,如果传入不同的文件*,则可以打印到文件,例如:
void print_my_stuff(FILE* file) {
fprintf( file,"Starting serial number [%ld]\n", serial_info_p->start_int_idx);
fprintf(file, "Current Serial number [%d]\n", serial_info_p->current_int_idx);
.
.
.
}
答案 4 :(得分:1)
编辑:我没注意到你需要一个C解决方案。我将留下这个答案供参考,但它显然需要C ++。
您可以创建一个新的流类,将输出发送到两个流。我在http://www.cs.technion.ac.il/~imaman/programs/teestream.html找到了这个实现。我没试过,但它应该有用。
以下是链接中的代码:
#include <iostream>
#include <fstream>
template<typename Elem, typename Traits = std::char_traits<Elem> >
struct basic_TeeStream : std::basic_ostream<Elem,Traits>
{
typedef std::basic_ostream<Elem,Traits> SuperType;
basic_TeeStream(std::ostream& o1, std::ostream& o2)
: SuperType(o1.rdbuf()), o1_(o1), o2_(o2) { }
basic_TeeStream& operator<<(SuperType& (__cdecl *manip)(SuperType& ))
{
o1_ << manip;
o2_ << manip;
return *this;
}
template<typename T>
basic_TeeStream& operator<<(const T& t)
{
o1_ << t;
o2_ << t;
return *this;
}
private:
std::ostream& o1_;
std::ostream& o2_;
};
typedef basic_TeeStream<char> TeeStream;
您可以这样使用它:
ofstream f("stackoverflow.txt");
TeeStream ts(std::cout, f);
ts << "Jon Skeet" << std::endl; // "Jon Skeet" is sent to TWO streams
答案 5 :(得分:0)
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
FILE *f = fopen("somefile.txt", "a+");
FILE *fp[] = { stdout, f };
int i = 0;
for (i = 0; i < ARRAY_LEN(fp); i++) {
fprintf(fp[i], "Starting serial number [%ld]\n", serial_info_p->start_int_idx);
fprintf(fp[i], "Current serial number [%ld]\n", serial_info_p->start_int_idx);
}
fclose(f);
答案 6 :(得分:0)
到目前为止,我会比人们的建议更激进,但也许对你来说太过分了。 ('inline'关键字是C99;如果您编码为C89,则可以省略它而没有太大后果。)
/*
** These could be omitted - unless you get still more radical and create
** the format strings at run-time, so you can adapt the %-24s to the
** longest tag you actually have. Plus, with the strings all here, when
** you change the length from 24 to 30, you are less likely to overlook one!
*/
static const char fmt_int[] = "%-24s [%d]\n";
static const char fmt_long[] = "%-24s [%ld]\n";
static const char fmt_str[] = "%-24s [%s]\n"; /* Plausible extra ... */
static inline void print_long(FILE *fp, const char *tag, long value)
{
fprintf(fp, fmt_long, tag, value);
}
static inline void print_int(FILE *fp, const char *tag, int value)
{
fprintf(fp, fmt_int, tag, value);
}
static inline void print_str(FILE *fp, const char *tag, const char *value)
{
fprintf(fp, fmt_str, tag, value);
}
static void dump_data(FILE *fp, const serial_info_t *info)
{
dump_long("Starting serial number", info->start_int_idx);
dump_int( "Current Serial number", info->current_int_idx);
/* ... and similar ... */
}
然后调用代码将调用dump_data()
一次(使用参数stdout
)选项1,2,3和两次(一次使用stdout
,一次使用文件指针输出文件)选项4。
如果参数的数量真的很大(达到数百个),我甚至会考虑一个编码类型和偏移信息的数据结构(offsetof
来自<stddef.h>
)和指向函数等的指针,以便在dump_data()
中只有一个循环迭代一个编码所有必要信息的结构。
您还可以通过对数据结构的所有整数成员使用相同的基本整数类型(示例中为long
)来简化生命。
告诉我你的流程图[代码]并隐藏你的表格[数据结构],我将继续神秘化。告诉我你的桌子,我通常不需要你的流程图;他们会很明显。
此代码的表驱动版本可能最终节省空间,并且在以相同方式更改一百个相关函数时会感到沮丧,而表格数据中的简单更改可能会修复整个批次。