在屏幕上打印和文本文件

发布于 2024-07-12 00:12:17 字数 846 浏览 11 评论 0原文

我需要将某些内容转储到文本文件中,并且需要在屏幕上显示相同的内容。 (我正在讲述一个 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 文件。

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

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

I need to dump the certain things into a text file and same has needs to be displayed on screen. (I'm telling about a C program utiltiy)
The menu option looks like following,

1. display AA parameters
2. display BB parameters
3. display CC parameters
4. dump all
5. Exit
Select option >

If they select 1/2/3, it just needs to displayed on screen only or if they select option #4,it need to display all the parameters one by one and same needs to dumped in a .txt file.

I know, we can use the printf and fprintf functions to display on screen and write it to text file respectively. The thing is that I've display more that 20 parameters and each have at least 20 sub-parameters.

I'm currently implemented as below,

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

Is there an easiest way to implement this to cut down the number of lines of code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

橘虞初梦 2024-07-19 00:12:17

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

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

我会写这样的东西:(

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

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

Edit: the C++ tag seems misleading, can someone remove it please? thanks :)

I use variadic macros to customize printf and friends.

I would write something like this:

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

(the name comes from the tee(1) utility)

烟燃烟灭 2024-07-19 00:12:17

这样的东西允许您添加任意数量的输出流,并允许在运行时通过修改 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;
}

Something like this allows you to add any number of output streams, and allows changing them at runtime simply by modifying the PrintTarget linked list.

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

Here's a definition of auxiliary functions:

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;
}
愛放△進行李 2024-07-19 00:12:17

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

You could also just pipe the output of your prorgam to the tee(1) command.

眼眸 2024-07-19 00:12:17

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

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

If you're writing a console application, you should be able to output to the screen (standard output) using something like:

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

This should enable you to move the code that prints your data to its own function, and to pass in a FILE* for it to print to. Then the function can print to the screen if you pass "stdout", or to a file if you pass in a different FILE*, e.g.:

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);
    .
    .
    .
}
︶葆Ⅱㄣ 2024-07-19 00:12:17

编辑:我没有注意到您需要 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

Edit: I didn't notice you needed a C solution. I'll leave this answer for reference, but it obviously requires C++.

You could create a new stream class that sends the output to two streams. I found an implementation of this at http://www.cs.technion.ac.il/~imaman/programs/teestream.html. I haven't tried it, but it should work.

Here's the code from the link:

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

You would use it like this:

ofstream f("stackoverflow.txt");
TeeStream ts(std::cout, f);
ts << "Jon Skeet" << std::endl; // "Jon Skeet" is sent to TWO streams
感悟人生的甜 2024-07-19 00:12:17

我会比人们迄今为止所建议的更激进,但也许这对你来说太过分了。 (“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)以及指向函数等的指针,这样在 dump_data() 中就只有一个循环,迭代一个编码所有必要信息的结构。

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

弗雷德·布鲁克斯 (Fred Brooks) 在《人月神话》(一本值得一读的书,如果你还没有读过的话,但一定要读二十周年纪念版)中的第 9 章中说道:

向我展示你的流程图[代码]并隐藏你的表格[数据结构],我将继续感到困惑。 给我看看你的表格,我通常不需要你的流程图; 它们会很明显。

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

I'd go more radical than what people have suggested so far, but maybe it is too much for you. (The 'inline' keyword is C99; you can omit it without much consequence if you code to 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 ... */
}

Then the calling code would call dump_data() once (with argument stdout) for options 1, 2, 3 and twice (once with stdout, once with file pointer for output file) for option 4.

If the number of parameters got truly huge (into the multiple hundreds), I'd even go as far as to consider a data structure which encoded type and offset information (offsetof from <stddef.h>) and pointers to functions and such like, so that there would be just a loop in dump_data() iterating over a structure which encodes all the necessary information.

You could also simplify life by using the same basic integer type (long in your example) for all the integer members of the data structure.

Fred Brooks in "Mythical Man Month" - a book well worth reading if you've not already done so, but make sure you read the Twentieth Anniversary edition - says in Chapter 9:

Show me your flowcharts [code] and conceal your tables [data structures], and I shall continue to be mystified. Show me your tables, and I won't usually need your flowcharts; they'll be obvious.

A table-driven version of this code could end up saving space, as well as frustration when having to change a hundred related functions in the same way whereas a simple change in the tabular data could have fixed the whole lot.

非要怀念 2024-07-19 00:12:17
#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);
#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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文