C言語関係掲示板

過去ログ

No.141.CSV形式の編集


No.898

CSV形式で
投稿者---プログラマー見習(2002/01/21 11:51:20)


ファイル名:test.txt
ファイル内容
21,A
,B
,C
22,E
,F
意味としては21歳の人がA、B、Cであり
22歳の人がD、Eである。
同じ年齢の人が複数人存在する場合、2人目以降は年齢は省略します。
これを以下のように同じ年齢の人が複数人存在しても2人目以降の年齢を
省略せず出力したいのです。
21,A
21,B
21,C
22,D
22,E
どなたか教えてください



No.899

Re:CSV形式で
投稿者---素人(2002/01/21 12:34:17)


いまいち仕様がハッキリしないので
(「入力ファイルの1行目は必ず年齢がある」や
「年齢が省略されいてる場合は、デリミタ文字で始まる」など)
入力ファイルの整合性チェックは省いています。

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

#define TRUE 0
#define FALSE -1
#define D_IF_BUF_LEN 256
#define D_IF_AGE_LEN 128 /* こんなにいらないと思いますが・・・ */

int main(void)
{
FILE *fp_in;
FILE *fp_out;
char buf[D_IF_BUF_LEN];
char temp_buf[D_IF_BUF_LEN]; /* 作業用バッファ */
char seps[] = ","; /* デリミタ文字 */
char *token;
char age[D_IF_AGE_LEN]; /* 取得した年齢格納バッファ */

/*********************************************/
/* 入力ファイルのオープン */
if((fp_in = fopen("test.txt","r")) == NULL)
{
return(FALSE);
}
/*********************************************/
/* 出力ファイルのオープン */
if((fp_out = fopen("result.txt","w")) == NULL)
{
return(FALSE);
}

while(fgets(buf,D_IF_BUF_LEN,fp_in) != NULL)
{
if(buf[0] != ',')
{
/*********************************************/
/* 年齢の取得 */
strcpy(temp_buf,buf);
token = strtok(buf,seps);
strcpy(age,token);
}
else
{
/*********************************************/
/* 取得した年齢とbufを結合 */
strcpy(temp_buf,age);
strcat(temp_buf,buf);
}
/*********************************************/
/* ファイルへの出力 */
fprintf(fp_out,"%s",temp_buf);
}
/*********************************************/
/* ファイルのクローズ */
fclose(fp_in);
fclose(fp_out);
/*********************************************/
/* 正常終了 */
return(TRUE);
}


No.906

Re:CSV形式で
投稿者---B.Smith(2002/01/21 20:43:09)


私の方からもご紹介しておきます。大筋においてはNo899の素人さんのプログラムと変わりありませんので、参考程度にご覧ください。

●ここでは、元となるテキストデータをメモリにすべてロードしています。それほど大きなデータでなければ、メモリ容量で問題を発生することはありません。もちろん、1件ずつ読み込んで処理しても良いのですが、実用的な面での理由は、
    ・1回の処理で開くファイル数を極力少なくする。
    ・ファイル操作の負荷軽減。

あらゆる処理で、ファイル操作のオーバヘッドは侮れません。処理内容にもよりますが、何十万件とあるデータを処理する場合、1件ずつの読み込みが入っただけで、全体の処理時間が殺人的に遅くなります。今回は、書き込みだけは処理の簡単のためにレコード単位で行っていますが、理想は1回だけの読み込みと、1回だけの書き込みです。

●切り出したトークンを一度数値に変更してみて、数値に変更できた場合は、それを年齢と見なしています。いきなり年齢ではなく、A、B、C…が出てきた場合、年齢の初期値として与えている-1が出力されます(エラーという意味になります)。

もちろん、この方法が必ず良いというわけではありません。恐らく、実際の仕様はもっと複雑でしょうから、目的にあったプログラムを作ってみてください。

サンプル.Microsoftコンパイラ版
#include <io.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* CSVファイル1レコードをとりあえず256バイトとする */
#define     MAX_RECORD  256

#define     SEPARATOR   ",\r\n" /* セパレータ */

char    *LoadFile(char *);
int     CreateCSVFile(char *,char *);

void main(void )
{
    char    *pTextFile;

    /* メモリに元データをロード */
    pTextFile = LoadFile("Test.Txt");
    if (!pTextFile){
        printf("fail.\n");
        return;
    }

    /* CSVファイルに編集 */
    if (!CreateCSVFile("Test.csv",pTextFile)){
        free(pTextFile);
        printf("fail.\n");
        return;
    }

    free(pTextFile);
    printf("Success.\n");
}

/* 元データをメモリにロード */
/* 戻り値のポインタがNULL以外(正常終了)の場合、使用後に   */
/* 関数freeにより解放すること                               */
char    *LoadFile(char *FileName)
{
    FILE    *fp;
    size_t  wSize;
    char    *ptr;

    fp = fopen(FileName,"r");   /* リードオープン */
    if (!fp)    return NULL;

    wSize = (size_t )filelength(fileno(fp));   /* ファイルサイズ */

    /* メモリの確保 */
    /* テキストデータとして扱うため、末尾ヌル分を余計に確保 */
    ptr = (char *)malloc(wSize + 1);
    if (!ptr){
        fclose(fp);
        return NULL;
    }

    fread(ptr,wSize,1,fp);  /* 読み込み */

    fclose(fp);

    *(ptr + wSize) = 0;

    return ptr;
}

/* CSVファイルに編集する */
int     CreateCSVFile(char *FileName,char *Buf)
{
    FILE    *fp;
    char    *tk,*pScanStop;
    char    Record[MAX_RECORD+1];
    int     Number,Age;

    fp = fopen(FileName,"w");   /* ライトオープン */
    if (!fp)    return 0;

    Age = -1;   /* 実際に出力する年齢 */

    /* トークンが切り出せなくなるまで繰り返す */
    tk = strtok(Buf,SEPARATOR);
    for(;tk;tk = strtok(NULL,SEPARATOR)){

        Number = (int )strtol(tk,&pScanStop,10);  /* 数値への変換 */

        /* 数値に変換できた場合、そのトークンは */
        /* 年齢項目であるとする                 */
        if (tk + strlen(tk) <= pScanStop){
            /* 出力する年齢が変わった時、新しい年齢を保存する */
            if (Number != Age)
                Age = Number;
            continue;   /* 年齢だけでは出力しない */
        }

        /* 編集と出力 */
        sprintf(Record,"%d,%s\n",Age,tk);
        fwrite(Record,strlen(Record ),1,fp);
    }

    fclose(fp);

    return 1;
}




戻る


「初心者のためのポイント学習C言語」 Last modified:2002.03.06
Copyright(c) 2000-2002 TOMOJI All Rights Reserved