在屏幕上打印和文本文件

时间:2009-01-14 18:21:05

标签: c text tee

我需要将某些内容转储到文本文件中,同样需要在屏幕上显示。 (我说的是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);

是否有最简单的方法来实现此目的以减少代码行数?

7 个答案:

答案 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)来简化生命。

弗雷德布鲁克斯在“神话人月”中 - 如果你还没有这本书,那么这本书值得一读,但请确保你阅读了二十周年纪念版 - 在第9章中说:

  

告诉我你的流程图[代码]并隐藏你的表格[数据结构],我将继续神秘化。告诉我你的桌子,我通常不需要你的流程图;他们会很明显。

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