2

特定のものをテキストファイルにダンプする必要があり、同じものを画面に表示する必要があります。(私はCプログラムユーティリティについて話しています)メニューオプションは次のようになります。

1.AAパラメータを表示
2.BBパラメータ表示
3. CC パラメータを表示する
4.すべてをダンプする
5.終了
オプションを選択 >

1/2/3 を選択した場合は、画面のみに表示するだけで済みます。オプション #4 を選択した場合は、すべてのパラメータを 1 つずつ表示し、同じように .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++ が必要です。

出力を 2 つのストリームに送信する新しいストリーム クラスを作成できます。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

私は人々がこれまでに提案したものよりも過激になりますが、多分それはあなたにとって多すぎます。('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に対して1dump_data()回(引数付き)、オプション4に対して2回(1回は、出力ファイル用のファイルポインターを使用して)呼び出します。stdoutstdout

パラメータの数が本当に(数百に)膨大になった場合は、型とオフセットの情報(offsetofから<stddef.h>)と関数へのポインタなどをエンコードしたデータ構造を検討することもできます。dump_data()必要なすべての情報をエンコードする構造体を反復処理するループです。

longデータ構造のすべての整数メンバーに同じ基本整数型(この例では)を使用することで、作業を簡素化することもできます。

「人月の神話」のフレッド・ブルックス-まだ読んでいない場合は読む価値のある本ですが、20周年記念版を必ず読んでください-第9章で次のように述べています。

あなたのフローチャート[コード]を見せて、あなたのテーブル[データ構造]を隠してください、そして私は謎に包まれ続けます。私にあなたのテーブルを見せてください、そして私は通常あなたのフローチャートを必要としません。それらは明らかです。

このコードのテーブル駆動バージョンは、スペースを節約するだけでなく、同じ方法で100の関連する関数を変更しなければならない場合のフラストレーションを節約する可能性がありますが、表形式のデータを変更するだけで全体を修正できた可能性があります。

于 2009-01-15T14:40:56.110 に答える
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 に答える