- ベストアンサー
差分ファイルを読み込んで解析する
刻々とデータが積み重なっていくファイルを、 任意のタイミングで読み取り、解析コードに通す。 データ取得はそれと平行して続いており、 次のタイミングで行う解析では、 以前と比べて新しく取得された部分のデータだけを通す…… ということをC言語を中心としたプログラムで行いたいのですが、 何か良い方法はないでしょうか。
- みんなの回答 (13)
- 専門家の回答
質問者が選んだベストアンサー
データを積み重ねたファイルのレコード件数が一定数 以上になったら、解析用に移す為のファイル名に変更 蓄積用ファイルは0件からデータの蓄積を再開。 解析用プログラムは解析用ファイルができるのを検知 して解析処理を実行して、終了したら解析ファイルを 削除または全データの累積ファイルに追加というのは?
その他の回答 (12)
- mikaemi
- ベストアンサー率50% (33/65)
バイト単位に読んだほうが安全かな?^^ ===== rd.c #include <stdio.h> #include <unistd.h> #include <stdlib.h> /* データが書き出されているか確認する */ int is_available(FILE *fp) { int c; clearerr(fp); if ((c = getc(fp)) == EOF) return 0; ungetc(c, fp); return 1; } /* sz バイト読み込むまで読み続け、確保したメモリに格納して返す */ void *read_bytes(FILE *fp, size_t sz) { static size_t bufsiz = 0; static unsigned char *buffer = NULL; unsigned char *p; if (sz > bufsiz) buffer = realloc(buffer, bufsiz = sz); p = buffer; while (sz > 0) { size_t rv; if ((rv = fread(p, 1, sz, fp)) == 0) { clearerr(fp); sleep(3); /* 適当な時間休む */ continue; } p += rv; sz -= rv; } return buffer; } /* 個数(int)を読んで、その個数分だけのデータ(int)を読み、標準出力に書き出す */ void read_print_data(FILE *fp) { int i, len, *pdata; len = *(int *)read_bytes(fp, sizeof len); if (len <= 0) return; pdata = read_bytes(fp, sizeof(int) * len); for (i = 0; i < len; ++i) printf("%d ", pdata[i]); } /* そのまま読む */ void read1(FILE *fp) { while (!is_available(fp)) sleep(3); printf("read1: "); read_print_data(fp); putchar('\n'); } /* ファイルをオープンし、シークしてから読む */ void read2(const char fn[], long *p) { FILE *fp = fopen(fn, "rb"); fseek(fp, *p, SEEK_SET); while (!is_available(fp)) sleep(3); printf("read2: "); read_print_data(fp); putchar('\n'); *p = ftell(fp); fclose(fp); } /* ファイルをオープンし、位置を設定してから読む */ void read3(const char fn[], fpos_t *pos) { FILE *fp = fopen(fn, "rb"); fsetpos(fp, pos); while (!is_available(fp)) sleep(3); printf("read3: "); read_print_data(fp); putchar('\n'); fgetpos(fp, pos); fclose(fp); } /* 3つの方法(オープンしっぱなし、シークする、位置指定する)で、 個数(int)・データ(int[2])のバイナリレコードを 10 回読み込んで標準出力に出す。 */ int main(void) { const int N = 10; int i; const char fn[] = "data.bin"; FILE *fp; long p; fpos_t pos; if ((fp = fopen(fn, "rb")) == NULL) { perror(fn); return 1; } p = ftell(fp); fgetpos(fp, &pos); for (i = 0; i < N; ++i) { switch (i % 6) { case 0: read1(fp); read2(fn, &p); read3(fn, &pos); break; case 1: read1(fp); read3(fn, &pos); read2(fn, &p); break; case 2: read2(fn, &p); read1(fp); read3(fn, &pos); break; case 3: read2(fn, &p); read3(fn, &pos); read1(fp); break; case 4: read3(fn, &pos); read1(fp); read2(fn, &p); break; case 5: read3(fn, &pos); read2(fn, &p); read1(fp); break; } } fclose(fp); return 0; } ==== wr.c #include <stdio.h> #include <unistd.h> /* 個数(int)、データ(int[2])のバイナリレコードを 10 回書き出す */ int main(void) { const int N = 10; int buf[2]; const int len = sizeof buf / sizeof buf[0]; const char fn[] = "data.bin"; FILE *fp; int i, j, k = 0; if ((fp = fopen(fn, "wb")) == NULL) { perror(fn); return 1; } for (i = 0; i < N; ++i) { sleep(10); /* 適当に合間を入れる */ for (j = 0; j < len; ++j) buf[j] = k++; fwrite(&len, sizeof len, 1, fp); fwrite(buf, sizeof buf[0], len, fp); fflush(fp); } fclose(fp); return 0; }
お礼
実際にソースコードの具体例まで挙げていただき、 色々と参考になりました。 ありがとうございました。 ただ、もう少し推敲してから回答を投稿してもらっていたら、 もっと読みやすくて分かりやすいものにしていただけたかな、 と思います。
- mikaemi
- ベストアンサー率50% (33/65)
プログラム自体が終了するので、なくてもかまいませんけど、rd.c の main() の最後の return 0; の前で、一応 fclose(fp); しておいてください^^ どうせ、エラーチェックはあまりしてないですけど。。。 実プログラムは、しっかりとエラーチェックしてくださいませ^^
- mikaemi
- ベストアンサー率50% (33/65)
あれ?rd.c 中で、if 文の後に変数宣言してるのに、gcc は通したな。。今は、C言語でも、ブロックの先頭じゃなきゃいけないことはなくて、どこでも変数宣言できるのか?^^ でも、まあ、一応、入れ替えておいてください。 ==== rd.c の main() の中の部分 FILE *fp; long p = 0; fpos_t pos; if ((fp = fopen(fn, "rb")) == NULL) { perror(fn); return 1; } fgetpos(fp, &pos);
- mikaemi
- ベストアンサー率50% (33/65)
ん。。socket() と select() の組み合わせのほうが、セマフォなんかよりポータビリティがあって、シグナルなんかより安全確実かもしれませんね^^ ではでは。ながらく失礼しました(笑)
- mikaemi
- ベストアンサー率50% (33/65)
>『同期を取る必要があって、…いろいろありますよね^^』 あぁ、データはファイル渡しなんだから、同期だけ取れればよいのか^^ 同期のためのファイル(空ファイルでいい)を決めておいて、そのファイルが存在するか存在しないかで同期をとってもいいですね。セマフォ・シグナルなどなどのプロセス間通信をつかってもいいですけど^^
- mikaemi
- ベストアンサー率50% (33/65)
あと、読み込む側が他にも仕事があるなら、最初の fread() で 0 が返ってきて EOF だったら、まだ書き出す側が書き込んでないと判定して、読み込む側が他の仕事をすればいいんじゃないですかね。
- mikaemi
- ベストアンサー率50% (33/65)
あっ。。read1、read2、read3 の二つ目の fread() の戻り値 sz が 0 のときは clearerr() を呼んでおいたほうがいいですね^^ 失礼しました。 ほんとは、fread() などが 0 を返したとき、無条件で clearerr() を呼ばず、エラーかEOFかを feof() で調べるのがいいと思いますけど。。。 あと、CPU の無駄遣いを防止するため、EOF ならちょっと sleep() を入れておくとか^^
- mikaemi
- ベストアンサー率50% (33/65)
ご参考: 先の※の条件でよければ、前に書いた3つの方法でもできますけどね^^ 同期を取る必要があって、プロセスに親子関係があるなら、パイプでくっつけておくとか、シグナルを送る(お薦めしませんが^^)とか。親子関係などなければ、セマフォを使って共有メモリを使うとか(データ量が少ないならメッセージキューとか)いろいろありますよね^^ === wr.c #include <stdio.h> #include <unistd.h> int main(void) { const int N = 10; int buf[2]; const int len = sizeof buf / sizeof buf[0]; const char fn[] = "data.bin"; FILE *fp; int i, j, k = 0; if ((fp = fopen(fn, "wb")) == NULL) { perror(fn); return 1; } for (i = 0; i < N; ++i) { sleep(10); for (j = 0; j < len; ++j) buf[j] = k++; fwrite(&len, sizeof len, 1, fp); fwrite(buf, sizeof buf[0], len, fp); fflush(fp); } fclose(fp); return 0; } ==== rd.c #include <stdio.h> #include <unistd.h> #include <stdlib.h> void read1(FILE *fp) { int len; size_t sz; printf("read1: "); while (fread(&len, sizeof len, 1, fp) == 0) clearerr(fp); while (len > 0) { int i; int buf[10]; size_t nbuf = sizeof buf / sizeof buf[0]; sz = fread(buf, sizeof buf[0], nbuf > len ? len : nbuf, fp); for (i = 0; i < sz; ++i) printf("%d ", buf[i]); len -= sz; } putchar('\n'); } void read2(const char fn[], long *p) { int len; size_t sz; FILE *fp = fopen(fn, "rb"); fseek(fp, *p, SEEK_SET); printf("read2: "); while (fread(&len, sizeof len, 1, fp) == 0) clearerr(fp); while (len > 0) { int i; int buf[10]; size_t nbuf = sizeof buf / sizeof buf[0]; sz = fread(buf, sizeof buf[0], nbuf > len ? len : nbuf, fp); for (i = 0; i < sz; ++i) printf("%d ", buf[i]); len -= sz; } putchar('\n'); *p = ftell(fp); fclose(fp); } void read3(const char fn[], fpos_t *pos) { int len; size_t sz; FILE *fp = fopen(fn, "rb"); fsetpos(fp, pos); printf("read3: "); while (fread(&len, sizeof len, 1, fp) == 0) clearerr(fp); while (len > 0) { int i; int buf[10]; size_t nbuf = sizeof buf / sizeof buf[0]; sz = fread(buf, sizeof buf[0], nbuf > len ? len : nbuf, fp); for (i = 0; i < sz; ++i) printf("%d ", buf[i]); len -= sz; } putchar('\n'); fgetpos(fp, pos); fclose(fp); } int main(void) { const int N = 10; int i; const char fn[] = "data.bin"; FILE *fp; if ((fp = fopen(fn, "rb")) == NULL) { perror(fn); return 1; } long p = 0; fpos_t pos; fgetpos(fp, &pos); for (i = 0; i < N; ++i) { switch (i % 6) { case 0: read1(fp); read2(fn, &p); read3(fn, &pos); break; case 1: read1(fp); read3(fn, &pos); read2(fn, &p); break; case 2: read2(fn, &p); read1(fp); read3(fn, &pos); break; case 3: read2(fn, &p); read3(fn, &pos); read1(fp); break; case 4: read3(fn, &pos); read1(fp); read2(fn, &p); break; case 5: read3(fn, &pos); read2(fn, &p); read1(fp); break; } } return 0; } ==== 実行結果 % gcc -o wr wr.c % gcc -o rd rd.c % ./wr & sleep 2; ./rd [1] 2592 read1: 0 1 read2: 0 1 read3: 0 1 read1: 2 3 read3: 2 3 …途中略… read3: 16 17 read2: 18 19 read3: 18 19 read1: 18 19
- mikaemi
- ベストアンサー率50% (33/65)
「…次のタイミングで何もせずに fp から読み出す」で、何もせずにではなくて、「もし前回のタイミングで EOF まで読んでいたら clearerr(fp) で EOF フラグを消してから」ですね^^ それと、 ※ 読み込むデータのバイト数は事前に決まっている(あるいは、何バイトか読めば書いてあるので、そのバイト数だけ読めばよい)ので、読み込む側のプログラムは、任意のタイミングで読んでも、決まったバイト数読み込めるまで読めばよいので、データを書き出すプログラムが書き出し中でもかまわない。 みたいな条件があるんですか?^^
- mikaemi
- ベストアンサー率50% (33/65)
『生成されるファイルは、…追加されていくだけ…。ただバイナリファイルなので、…』とありましたが、<stdio.h> の FILE *fp を使って読み書きしてるとして、 ・前回のタイミングで読んだときに fp をクローズしないでそのままにしておき、次のタイミングで何もせずに fp から読み出す とか、 ・前回のタイミングで読み終わったとき、p = ftell(fp); fclose(fp); し、次のタイミングで fp = fopen(fname, "rb"); fseek(fp, p, SEEK_SET); して fp から読み出す とか、 ・前回のタイミングで読み終わったとき、fgetpos(fp, &pos); fclose(fp); し、次のタイミングで fp = fopen(fname, "rb"); fsetpos(fp, &pos); して fp から読み出す とかでよいということですか??
- 1
- 2
補足
なるほど、ファイルを作るそばから 変更していくとは思いつきませんでした。 比較的容易に実現できそうですが、 理想は自分の好きなタイミングなんですよ。 それでも、タイミングは本質的に重要な部分ではないし、 うまく使えば活用できそうなアイデアですね。