掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板2

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.28687

CSVファイルからの読み込み
投稿者---こう(2006/11/02 20:38:32)


カンマ区切りのCSVファイルから読み込みをしています。
一応、動きますが、無駄なことをやっているのが多いです。
できれば、スマートにしたいのですが、直したらよいところとか
教えて下さい。

環境は、
Solaris8
gcc でコンパイル

読み込むデータ
--------------------
ID0001,○,○
ID0002,○,○
ID0003,○,○
ID0004,○,
ID0005,,
ID0006,,○
ID0007,,○
ID0008,,

#include <stdio.h>

typedef struct {
    char id[7];
    char val1[3];
    char val2[3];
} DATA;

main()
{
    FILE *fp;
    DATA data[10];
    char buff[128];
    char *pr;
    char *pr_tmp;
    int  i;

    memset(buff,'\0',sizeof(buff));
    memset(data,'\0',sizeof(DATA)*10);

    if ((fp = fopen("test.csv","r")) == NULL) {
        printf( "FileError: file open failed (%s)\n","test.csv");
        exit(1);
    }

    i = 0;
    memset(buff,'\0',sizeof(buff));
    while(fgets(buff,sizeof(buff),fp) != NULL) {
        pr = strchr(buff,',');
        strncpy(data[i].id,buff,pr-buff);

        pr_tmp = pr;
        pr = strchr(pr_tmp+1,',');
        strncpy(data[i].val1,pr_tmp+1,pr-(pr_tmp+1));

        pr_tmp = pr;
        strcpy(data[i].val2,pr_tmp+1);

printf("Debug [%s][%s][%s]\n",data[i].id,data[i].val1,data[i].val2);
        i++;
        memset(buff,'\0',sizeof(buff));
    }

    fclose(fp);
    return(0);
}




この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:CSVファイルからの読み込み 28688 あかま 2006/11/02 21:51:31
<子記事> Re:CSVファイルからの読み込み 28689 かずま 2006/11/02 23:42:22
<子記事> Re:CSVファイルからの読み込み 28695 こう 2006/11/03 10:40:42


No.28688

Re:CSVファイルからの読み込み
投稿者---あかま(2006/11/02 21:51:31)


綺麗に書けてると思いますがバグがあります。

基本的にmemsetはこのプログラムでいらないので,
なんとなくバグ消しにmemsetを使っている気がするので
なぜmemsetでバグが消えるのか説明しておきます(わかっててやってたらすいません)。

strncpyはコピーする文字列が指定された長さ以上の時、末尾に'\0'をつけません。
なのでこのプログラムの場合は明示的に'\0'をつける必要がありますが、
memset(data,'\0',sizeof(DATA)*10);
で'\0'に初期化されているので事実上必要ありません。

さて、memsetでないバグもあります。
DATA構造体のval2[3]にデータをコピーするところが問題です。
fgets()で文字列を読み込んだとき文字列末尾に'\n'がついています。
ですのでstrcpyでコピーされるのは"○\n"となり'\0'含めて4byte必要です。
構造体は宣言したサイズよりちょっと大きめに取られることがあるので(パティングといいます)
たまたま空きスペースがあり4byteをval2に書き込んでもエラーになりません。
'\n'は事前に除去しておきましょう。

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

typedef struct {
    char id[7];
    char val1[3];
    char val2[3];
} DATA;

main()
{
    FILE *fp;
    DATA data[10];
    char buff[128];
    char *pr;
    char *pr_tmp;
    int  i;

    //memset(buff,'\0',sizeof(buff));memsetは全部いらない

    //memset(data,'\0',sizeof(DATA)*10);


    if ((fp = fopen("test.csv","r")) == NULL) {
        printf( "FileError: file open failed (%s)\n","test.csv");
        exit(1);
    }

    i = 0;
    //memset(buff,'\0',sizeof(buff));

    while(fgets(buff,sizeof(buff),fp) != NULL) {
        
        if(buff[strlen(buff)-1] == '\n') buff[strlen(buff)-1] = '\0';//文字列末の'\n'を除去

        
        pr = strchr(buff,',');
        strncpy(data[i].id,buff,pr-buff);
        data[i].id[pr-buff] = '\0';//'\0'を入れる

        
        pr_tmp = pr;
        pr = strchr(pr_tmp+1,',');
        strncpy(data[i].val1,pr_tmp+1,pr-(pr_tmp+1));
        data[i].id[pr-buff] = '\0';
        
        pr_tmp = pr;
        strcpy(data[i].val2,pr_tmp+1);

        printf("Debug [%s][%s][%s]\n",data[i].id,data[i].val1,data[i].val2);
        i++;
        //memset(buff,'\0',sizeof(buff));

    }

    fclose(fp);
    return(0);
}



この投稿にコメントする

削除パスワード

No.28689

Re:CSVファイルからの読み込み
投稿者---かずま(2006/11/02 23:42:22)


読み込むデータが 11個以上あったらどうなりますか?
「,」で区切った後の文字列が 7バイトや 3バイト以上あったらどうなりますか?
「,」が見つからなかったらどうなりますか?
そんなことはありえないという仕様ならそれでもいいし、
そうでなければ、エラーメッセージを出して処理を打ち切るようにしてもいいし、
余分なデータを無視するなど、適切な処理をするのもいいでしょう。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE  10

typedef struct {
    char id[7];
    char val1[3];
    char val2[3];
} DATA;

char *set_field(char *target, int size, char *buf)
{
    int len = strcspn(buf, ",\n");
    if (len < size) size = len;
    memcpy(target, buf, size);
    target[size] = '\0';
    return buf + len + (buf[len] != '\0');
}

int main(void)
{
    DATA data[SIZE];  char buff[128];  int  i;

    FILE *fp = fopen("test.csv", "r");
    if (!fp) puts("FileError: file open failed (test.csv)"), exit(1);

    for (i = 0; i < SIZE && fgets(buff, sizeof buff, fp); i++) {
        char *p = set_field(data[i].id, sizeof(data[i].id)-1, buff);
        p = set_field(data[i].val1, sizeof(data[i].val1)-1, p);
        set_field(data[i].val2, sizeof(data[i].val2)-1, p);
        printf("Debug [%s][%s][%s]\n",
                data[i].id, data[i].val1, data[i].val2);
    }
    fclose(fp);
    return 0;
}



この投稿にコメントする

削除パスワード

No.28695

Re:CSVファイルからの読み込み
投稿者---こう(2006/11/03 10:40:42)


あかま、かずまさん
説明ありがとうございます。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧