печать на экране и текстовый файл

Мне нужно сбросить определенные вещи в текстовый файл, и то же самое нужно отобразить на экране. (Я говорю об утилите программы 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);

Есть ли самый простой способ реализовать это, чтобы сократить количество строк кода?


person Thi    schedule 14.01.2009    source источник
comment
Я так понимаю просто трубка через тройник не вариант?   -  person falstro    schedule 15.01.2009


Ответы (6)


Я бы пошел более радикально, чем то, что люди предлагали до сих пор, но, возможно, это слишком много для вас. (Ключевое слово «встроенное» — это 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:

Покажи мне свои блок-схемы [код] и спрячь свои таблицы [структуры данных], и я продолжу пребывать в замешательстве. Покажите мне свои таблицы, и обычно мне не понадобятся ваши блок-схемы; они будут очевидны.

Табличная версия этого кода может в конечном итоге сэкономить место, а также вызвать разочарование, когда придется изменять сотню связанных функций одним и тем же способом, тогда как простое изменение табличных данных могло исправить все.

person Jonathan Leffler    schedule 15.01.2009

Редактировать: тег C++ вводит в заблуждение, может кто-нибудь удалить его, пожалуйста? спасибо :)

Я использую вариативные макросы для настройки printf и друзей.

Я бы написал что-то вроде этого:

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

(название происходит от утилиты tee(1))

person Giacomo    schedule 14.01.2009

Что-то вроде этого позволяет вам добавлять любое количество выходных потоков и позволяет изменять их во время выполнения, просто изменяя связанный список 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;
}
person Eclipse    schedule 14.01.2009
comment
Не помешали бы небольшие функции PrintTarget* pnt_create(PrintTarget* head, FILE* target) и void pnt_destroy(PrintTarget* list). - person jfs; 14.01.2009
comment
Это действительно хороший алгоритм. Хотя я не уверен, поможет ли это сократить количество строк кода, поскольку проблема касается только двух разных потоков вывода. Тем не менее, это хорошо продуманное решение. - person Chris; 14.01.2009
comment
@Josh: я добавил примеры определений pntCreate(), pntDestroy(). Смело откатывайтесь. - person jfs; 14.01.2009

Вы также можете просто передать вывод своей программы команде tee(1).

person Adam Rosenfield    schedule 14.01.2009

Если вы пишете консольное приложение, вы должны иметь возможность вывода на экран (стандартный вывод), используя что-то вроде:

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

Это должно позволить вам переместить код, который печатает ваши данные, в свою собственную функцию и передать ФАЙЛ* для печати. Затем функция может печатать на экране, если вы передаете «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);
    .
    .
    .
}
person MattK    schedule 14.01.2009

Редактировать: я не заметил, что вам нужно решение 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
person Matthew Crumley    schedule 14.01.2009
comment
О боже, миру больше НЕ нужен Зэпп Браниган. Один слишком много. - person Adam Rosenfield; 14.01.2009
comment
@ Адам, а как насчет двух Джонов Скитса? Я не знаю, сможет ли Stack Overflow справиться с этим. - person Matthew Crumley; 15.01.2009