2

我需要将某些内容转储到文本文件中,并且需要在屏幕上显示相同的内容。(我说的是 C 程序实用程序)菜单选项如下所示,

1.显示AA参数
2.显示BB参数
3.显示CC参数
4.全部转储
5. 退出
选择选项 >

如果他们选择1/2/3,它只需要显示在屏幕上,或者如果他们选择选项#4,它需要一个一个显示所有参数,同样需要转储到一个.txt文件中。

我知道,我们可以使用 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);

有没有一种最简单的方法来实现这一点以减少代码行数?

4

7 回答 7

6

编辑:C++ 标签似乎具有误导性,有人可以删除它吗?谢谢 :)

我使用可变参数宏来自定义 printf 和朋友。

我会写这样的东西:

#define     tee(fp,fmt, ...)                             \
        {                                                \
                printf (fmt, __VA_ARGS__);               \
                fprintf (fp, fmt, __VA_ARGS__);          \
        }

(名称来自 tee(1) 实用程序)

于 2009-01-14T18:42:19.023 回答
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;
}
于 2009-01-14T18:43:39.520 回答
2

您也可以将程序的输出通过管道传输到tee(1)命令。

于 2009-01-14T18:50:29.180 回答
1

如果您正在编写控制台应用程序,您应该能够使用以下内容输出到屏幕(标准输出):

fprintf(stdout, "Hello World\n");

这应该使您能够将打印数据的代码移动到它自己的函数中,并传递一个 FILE* 以便它打印到。然后,如果您传递“stdout”,则该函数可以打印到屏幕,或者如果您传递不同的 FILE*,则该函数可以打印到文件,例如:

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);
    .
    .
    .
}
于 2009-01-14T18:32:59.807 回答
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
于 2009-01-14T18:46:14.507 回答
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);
于 2009-01-14T18:31:14.180 回答
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 ... */
}

然后调用代码将为选项 1、2、3调用dump_data()一次(带参数),为选项 4 调用两次(一次为,一次为输出文件的文件指针)。stdoutstdout

如果参数的数量真的很大(达到数百个),我什至会考虑一个数据结构,它编码类型和偏移信息(offsetoffrom <stddef.h>)以及指向函数等的指针,这样就会有只是一个循环dump_data()遍历编码所有必要信息的结构。

long您还可以通过对数据结构的所有整数成员使用相同的基本整数类型(在您的示例中)来简化生活。

Fred Brooks 在“神话人物月”中——如果你还没有读过这本书,非常值得一读,但请确保你阅读了 20 周年纪念版——在第 9 章中说:

给我看你的流程图[代码]并隐藏你的表格[数据结构],我将继续被迷惑。给我看你的表格,我通常不需要你的流程图;他们会很明显。

此代码的表格驱动版本最终可能会节省空间,并且在必须以相同方式更改一百个相关函数时感到沮丧,而表格数据中的简单更改可能会解决整个问题。

于 2009-01-15T14:40:56.110 回答