No.5161

一致するキーごとの集計
投稿者---hide(2003/02/14 10:50:23)


ワークファイルから読み込んだ値で項目が"aaa","bbb","ccc","ddd","eee"の5項目がありそのうち"eee"が金額とします。
このときに"aaa"から"ddd"までが一致する行で金額("eee")の集計をし、別のワークファイルに書き出しをする操作をしたいのですがどんな書き方をするといいですか?記述方法を教えていただけないでしょうか。
自分で作れたのは下のようなところまででそれ以降がわからず手がつかないところです。よろしくお願いします。
/*test.h*/

/*********************************
*<ソートワーク入力>用 構造体 *
*********************************/
typedef struct t_insortwk {
char aaa[10];
char bbb[10];
char ccc[3];
char ddd[16];
char eee[15];
  char cr_return[2];/*改行*/

} T_INSORTWK;

/*********************************
*<ソートワーク出力>用 構造体 *
*********************************/
typedef struct t_outsortwk {
char aaa[10];
char bbb[10];
char ccc[3];
char ddd[16];
char eee[15];
  char cr_return[2];/*改行*/
} T_OUTSORTWK;

/*test1.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "test1.h"

int main(void)
{
FILE *fp;
FILE *fpo;
T_INSORTWK inwk;

T_OUTSORTWK outwk;
int i = 0;
if((fp = fopen("work3","r"))==NULL){
printf("ファイルを開くことができません\n");
exit(EXIT_FAILURE);
}

while(i==0){

if(fgets(inwk.aaa,10,fp)==NULL)break;
if(fgets(inwk.bbb,10,fp)==NULL)break;
if(fgets(inwk.ccc,3,fp)==NULL)break;
if(fgets(inwk.ddd,16,fp)==NULL)break;
if(fgets(inwk.eee,15,fp)==NULL)break;/*金額*/
if(fgets(inwk.cr_return,2,fp)==NULL)break;

if((fp = fopen("work4","w"))==NULL){
printf("ファイルを開くことができません\n");
exit(EXIT_FAILURE);
}
.
.
.


No.5166

Re:一致するキーごとの集計
投稿者---雪猫(2003/02/14 13:15:07)


こんにちわ 雪猫です♪
仕事が忙しくてもここは欠かさず見ていたりします(^^;)

ちょっと疑問に思ったことがありましたので質問でぇ〜す☆

>このときに"aaa"から"ddd"までが一致する行で金額("eee")の集計をし・・・
この、「一致する行」っとありますが、なにと一致させるんでしょう???
予め入力してある項目? 計算時に取得する項目?
それとも、別ファイルがあって、そこからさらに呼び出しした項目?

>別のワークファイルに書き出しをする操作
なんの項目を書き出すのでしょう? 計算した結果だけ?

以上でっすm(__)mペコリ
☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★

No.5167

Re:一致するキーごとの集計
投稿者---hide(2003/02/14 14:07:39)


質問ありがとうございます。

>>このときに"aaa"から"ddd"までが一致する行で金額("eee")の集計をし・・・
aaa=aaa&bbb=bbb&ccc=ccc&ddd=dddというような感じです。
文章だとわかりにくいかもしれませんが
取得するワークファイルのイメージは
5項目行が一列にそれぞれ桁数分ならんでおりその後ろに改行が入ります。
その行が何行もあり("_"は空白一文字を表しています。
↓は改行("\n")のつもりです)

/*読取ファイル*/
aaa...(10桁分)bbb...(10桁分)ccc(3桁分)ddd...(16桁分)eee...(15桁分)↓
111_______222_______33_444_____________123____________↓ ・・・
111_______222_______33_444_____________321____________↓ ・・・
112_______223_______33_444_____________123____________↓ ・・・

,鉢△旅圓"aaa"から"ddd"までが一致してるので集計してeeeは444。
残りは他の行と異なるためそのままです。
結果書き込みファイルには下記のように2行になって出力されます。

/*書き込みファイル*/
aaa...(10桁分)bbb...(10桁分)ccc(3桁分)ddd...(16桁分)eee...(15桁分)↓
111_______222_______33_444_____________444____________↓ ・・・
112_______223_______33_444_____________123____________↓ ・・・

なんとか説明になったでしょうか?
また質問があればお願いします。







No.5171

Re:一致するキーごとの集計
投稿者---hide(2003/02/14 15:21:15)


>例としてはcccの3桁の部分に2桁しか入っていない物を示していますがもし
>3桁の数字が入ると...
つながります。
よって改行があることで改行文字をチェックしてくれるテキスト関数fgetsを
使って読みこんでいます。

ソートされたテキストファイルを読むので
おそらく一致する行は続いてると思われます。

よろしくお願いします。

No.5182

Re:一致するキーごとの集計
投稿者---かずま(2003/02/15 15:30:52)


> ソートされたテキストファイルを読むので
> おそらく一致する行は続いてると思われます。

それなら、簡単。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define WIDTH  (10 + 10 + 3 + 16)

void proc(FILE *fin, FILE *fout)
{
    char buf[256], prev[WIDTH];  int val = 0;

    prev[0] = '\0';
    while (fgets(buf, sizeof buf, fin))
        if (memcmp(buf, prev, WIDTH) == 0)
            val += atoi(buf + WIDTH);
        else {
            if (prev[0])
                fprintf(fout, "%.*s%-16d\n", WIDTH, prev, val);
            memcpy(prev, buf, WIDTH);
            val = atoi(buf + WIDTH);
        }
    if (prev[0])
        fprintf(fout, "%.*s%-16d\n", WIDTH, prev, val);
}

int main(void)
{
    proc(stdin, stdout);
    return 0;
}


No.5194

Re:一致するキーごとの集計
投稿者---かずま(2003/02/15 19:31:31)


> /*読取ファイル*/
> aaa...(10桁分)bbb...(10桁分)ccc(3桁分)ddd...(16桁分)eee...(15桁分)↓

eee は 15桁だったんですね。
2つの fprintf の中の "%-16d" を "%-15d" にしてください。

No.5169

Re:一致するキーごとの集計
投稿者---雪猫(2003/02/14 14:54:58)


お昼の眠たい時間いかがお過ごしでしょうか? 雪猫です♪

読み込みファイルの中で、それぞれの項目が同じなら金額項目を加算して、
出力するって感じですね♪

ん〜と、ファイルから項目を取得する時、
1行ごとに取得して、それを項目ごとに分割したほうがよろしいのでは
ないでしょうか?
構造体の中の、改行コードの保存がいまいち理解できなくて・・・(><;

私なら、1行取得 それを項目ごとに分割して1次退避、次の行を読み込み、
同じように分割 それを退避していた前の項目と照らし合わせ、同じだったらば
金額の項目を加算 ・・・っという風に文章で書いてから進めています♪
そうすると、何がわからないのか一目瞭然ですぅ〜

それで、これって例えば、1行目と999999行目が同じって場合もありえるのでしょか? 1行目と2行目の項目が違うならば1行目と同じ物はない!!
っと断言できないとプログラムもまた別になるのかなぁっと思ったりしてます♪

でわ、がんばってプログラム作ってくださいね♪

No.5172

Re:一致するキーごとの集計
投稿者---hide(2003/02/14 15:30:40)


>お昼の眠たい時間いかがお過ごしでしょうか? 雪猫です♪
仕事がすすまず眠気がでなくて助かってます...

>読み込みファイルの中で、それぞれの項目が同じなら金額項目を加算して、
>出力するって感じですね♪
はい
>ん〜と、ファイルから項目を取得する時、
>1行ごとに取得して、それを項目ごとに分割したほうがよろしいのでは...
>構造体の中の、改行コードの保存がいまいち理解できなくて・・・(><;
改行によって一行分であることを判断させるようです。
テキスト関数fgets関数がそれにあたります。

>私なら、1行取得 それを項目ごとに分割して1次退避、次の行を読み込み、
>同じように分割 それを退避していた前の項目と照らし合わせ、同じだったらば
>金額の項目を加算 ・・・っという風に文章で書いてから進めています♪
>そうすると、何がわからないのか一目瞭然ですぅ〜
そういう書き方もあるんですね。
考えてみます。
>それで、これって例えば、1行目と999999行目が同じって場合もありえる>のでしょか? 1行目と2行目の項目が違うならば1行目と同じ物はない!!
>っと断言できないとプログラムもまた別になるのかなぁっと思ったりしてます♪
一応、ソートされたものを読むのでこの場合はないと思って大丈夫だと思いますが断言!とまでは・・・
>でわ、がんばってプログラム作ってくださいね♪
はい。

No.5173

Re:一致するキーごとの集計
投稿者---雪猫(2003/02/14 15:54:23)


ためしに、1行取得して、それを分割して保存するとこまで♪

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct buffer{
	char a[11];
	char b[11];
	char c[4]; 
	char d[17]; 
	char e[16];
}; 

int main(void)
{
	FILE *fp;
	struct buffer buff;
	char wk[100];

	if((fp = fopen("work.txt","r"))==NULL){
		printf("ファイルを開くことができません\n");
		return 0;
	}

	while (fgets(wk, 256, fp) != NULL) {
		printf("%s", wk);
		strncpy(buff.a,wk,10);
		strncpy(buff.b,wk+10,10);
		strncpy(buff.c,wk+20,3);
		strncpy(buff.d,wk+23,16);
		strncpy(buff.e,wk+39,15);

		buff.a[10] = '\0';
		buff.b[10] = '\0';
		buff.c[3] = '\0';
		buff.d[16] = '\0';
		buff.e[15] = '\0';
	}
	fclose(fp);
	return 0;
}


こ〜んな感じでできるのかな?
っというか、hideさんが作ろうとしているプログラムと似たプログラム
作ったことあるんですけど・・・忘れちゃいました えへっ★ミ

No.5177

Re:一致するキーごとの集計
投稿者---hide(2003/02/14 16:43:56)


サンプルありがとうございます。
さっそく参考にさせていただきます。

仕事がんばってください。


No.5181

Re:一致するキーごとの集計
投稿者---カンナ(2003/02/15 09:59:49)
http://hana.sakura.ne.jp/~elfin/k/


 こんな感じでどうでしょう?
 入力データは正しいものと仮定しています。そうでない場合はfgetsの
前後でそれなりの処理をしてください。

 私も仕事で似たようなのを組んだことがあります(COBOLでですが)。
そのときのはここでいうならaaaが一致、aaa・bbbが一致、aaa〜cccが一致、
aaa〜dddが一致の4段階の集計結果を書き出すようなもので、これよりも
さらに面倒でした。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* ソートワーク構造体 */
#define AAA_LEN        (10)
#define BBB_LEN        (10)
#define CCC_LEN        (3)
#define DDD_LEN        (16)
#define EEE_LEN        (15)

#define RECORD_LEN    (AAA_LEN+BBB_LEN+CCC_LEN+DDD_LEN+EEE_LEN)

typedef struct _sortwork
{
    char aaa[AAA_LEN+1];
    char bbb[BBB_LEN+1];
    char ccc[CCC_LEN+1];
    char ddd[DDD_LEN+1];
    char eee[EEE_LEN+1];
} sortwork_t;

/* プロトタイプ宣言 */
void summarize(FILE* fi, FILE* fo);
void sortwork_fill(sortwork_t* wk, char* buf);
int sortwork_compare(sortwork_t* lhs, sortwork_t* rhs);
void write_result(FILE* fo, sortwork_t* prev, int sum);

/* main */
int main()
{
    FILE* fi;
    FILE* fo;

    fi = fopen("input.txt", "r");
    fo = fopen("output.txt", "w");

    summarize(fi, fo);

    fclose(fi);
    fclose(fo);

    return 0;
}

/* fiファイルを集計してfoファイルに出力する */
void summarize(FILE* fi, FILE* fo)
{
    sortwork_t curr = {0};    /* 現在データ */
    sortwork_t prev = {0};    /* 前回データ(集計キー比較用) */
    int sum = 0;              /* 集計値 */
    char buf[RECORD_LEN+2+1]; /* 読込バッファ */

    /* 初回読込 */
    if ( !fgets(buf, RECORD_LEN+2+1, fi) )
    {
        return;
    }
    /* 読み込んだ値をcurrに格納 */
    sortwork_fill(&curr, buf);

    /* 集計キー比較用に現在データを前回データに退避 */
    prev = curr;

    /* 集計値の初期化 */
    sum = atoi(curr.eee);

    /* ソート関数が読み込めなくなるまで */
    while ( fgets(buf, RECORD_LEN+2+1, fi) )
    {
        /* 読み込んだ値をcurrに格納 */
        sortwork_fill(&curr, buf);

        /* 集計キーを比較 */
        if ( sortwork_compare(&prev, &curr) == 0 )
        {
            /* 集計キーが同じ場合 */

            /* eeeを集計値に加算する */
            sum += atoi(curr.eee);
        }
        else
        {
            /* 集計キーが代わった場合 */

            /* 現在の集計結果をfoに出力する */
            write_result(fo, &prev, sum);

            /* 現在データを前回データに退避 */
            prev = curr;

            /* 集計値の初期化 */
            sum = atoi(curr.eee);
        }
    }

    /* 最後の結果をfoに出力する */
    write_result(fo, &prev, sum);
}

/* 読み込んだ値をソートワークに格納する */
void sortwork_fill(sortwork_t* wk, char* buf)
{
    strncpy(wk->aaa, buf, AAA_LEN); buf += AAA_LEN;
    strncpy(wk->bbb, buf, BBB_LEN); buf += BBB_LEN;
    strncpy(wk->ccc, buf, CCC_LEN); buf += CCC_LEN;
    strncpy(wk->ddd, buf, DDD_LEN); buf += DDD_LEN;
    strncpy(wk->eee, buf, EEE_LEN);
}

/* ソートワーク同士を比較する */
int sortwork_compare(sortwork_t* lhs, sortwork_t* rhs)
{
    return !((strcmp(lhs->aaa, rhs->aaa) == 0 ) &&
             (strcmp(lhs->bbb, rhs->bbb) == 0 ) &&
             (strcmp(lhs->ccc, rhs->ccc) == 0 ) &&
             (strcmp(lhs->ddd, rhs->ddd) == 0 ));
}

/* 集計結果をファイルへ出力する */
void write_result(FILE* fo, sortwork_t* prev, int sum)
{
    int i;
    char sumBuf[EEE_LEN+1] = { 0 };

    fputs(prev->aaa, fo);
    fputs(prev->bbb, fo);
    fputs(prev->ccc, fo);
    fputs(prev->ddd, fo);

    itoa(sum, sumBuf, 10);

    /* sumBufの末尾を空白埋めする */
    for ( i = 0; i < EEE_LEN; ++i )
    {
        if ( !sumBuf[i] ) sumBuf[i] = ' ';
    }
    fputs(sumBuf, fo);
    fputs("\n", fo);
}


No.5220

Re:一致するキーごとの集計
投稿者---hide(2003/02/17 01:29:38)


返事ありがとうございます。
> 私も仕事で似たようなのを組んだことがあります(COBOLでですが)。
>そのときのはここでいうならaaaが一致、aaa・bbbが一致、aaa〜cccが一致、
>aaa〜dddが一致の4段階の集計結果を書き出すようなもので、これよりも
>さらに面倒でした。
実は自分も4段階あるんです…
ただその前に一つもできないのでとりあえずヒントを得たいと思い
投稿しました。
Cは初めてくむのでよくわかりません。
しかし、仕事なので何もしないわけにはいかず…
さっそく参考にさせていただきます。
ありがとうございます。


C言語関係掲示板

過去ログ

No.500.一致するキーごとの集計

[戻る] [ホームページ]
No.5223

Re:一致するキーごとの集計(質問)
投稿者---hide(2003/02/17 10:41:37)


カンナさんのサンプルを参考に構築させていただいてます。
そこで質問なんですが、実際の操作では、読み込んだ行を何項目かのキーで一致した順にそのまま明細としてはきだし
そのなかからさらにキーを絞って小計1,小計2,合計などださないといけないのですが、カンナさんのサンプルを使ってWhile文にifをつけたりしてなんとかできますか?
それとも、もっと複雑になってしまうのでしょうか?
それと、
/*最後の結果をfoに出力する*/
というところの結果がちゃんと得られず、削ってみましたが特に
問題ないように思えたのですが、削るという方向は問題ないでしょうか?

よろしくお願いします。