• 締切済み

C言語でのCSVファイルの読み出し方法

C言語の勉強をしています。 test.csvというファイル名のCSVファイルで 項目,名前,身長,体重,血液型,合否(合格なら○不合格なら空欄) 1,太郎,150,55.6,A,○ 2,二郎,165.5,60,B 3,三郎,160.2,59.5,AB,○ と書かれたファイルを読もうと思いまして #include <stdio.h> #define i 1000 #define j 1000 main() { int d[i][j]; double... FILE *fp; fp=fopen(fp,"test.csv","r"); for(i=0;i<=2;i++) { for(j=0;j<=6;j++) { fscanf(fp,"%d",d[i][j]); } }... という感じで書いても読み込みません。 どのように書いたら読み込むでしょうか。 Cをはじめて間もないのでどなたか教えていただけないでしょうか よろしくお願いします。

みんなの回答

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.9

>各行内に空のセルがある場合 空のセルがある場合、strtokは使えません。 sscanfなら、空のセルにも対応できますが、カンマを含むセルがある場合はやはり対応できません。 1,あ,"a,b" というのです。 さらに、規格上は、改行を含むセル、なんてのも存在します。こうなると、fgetsすら使えません。 なので、本気でやろうと思ったら、既存の関数はあてにならず、全部自力で処理する必要があります。 とはいえ、実際にはある程度フォーマットを限定できる場合が多いです。 ご提示の例であれば、fgetsとsscanfの組み合わせが現実的だと思います。

noname#137556
noname#137556
回答No.8

#7 です。間違えました。 誤: ret = sscanf(buf, "%d,%[^,],%lf,%lf,%[^,],%[^,^\n]", &n, s1, &x, &y, s2, s3); 正: ret = sscanf(buf, "%d,%[^,],%lf,%lf,%[^,],%[^,\n]", &n, s1, &x, &y, s2, s3); # あと、カンマ入の要素は言うまでもなく、空の要素があったりすると破綻します。 # 個人的なちょっとしたデータ処理ぐらいになら使えるかも。

noname#137556
noname#137556
回答No.7

scanf 系の関数でも一応できます。書式指定文字列中の "%[^,]" は ',' 以外の文字にマッチします。 char buf[256] = "1,太郎,150,55.6,A,○\n"; // fgets で読んだつもりのデータ // char buf[256] = "2,二郎,165.5,60,B"; int n, ret; double x, y; char s1[256], s2[256], s3[256] = ""; ret = sscanf(buf, "%d,%[^,],%lf,%lf,%[^,],%[^,^\n]", &n, s1, &x, &y, s2, s3); // 表示してみます printf("%d\n", ret); printf("%d\n", n); puts(s1); printf("%f\n", x); printf("%f\n", y); puts(s2); puts(s3); 上では手抜きしてますが、最低限、文字列を格納するバッファがオーバーフローしないようにすることと、sscanf の戻り値のチェックぐらいは必要です。

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.6

ちなみに、データがない可能性があるのが最後の合否部分だけなら、こんな邪道回答もあります。 前提として構造体・共用体を知っている必要はあります。 #include <stdio.h> #define LINE_LEN 256 typedef union {  struct {   char *num;   char *name;   char *height;   char *weight;   char *blood;   char *result;  } param;  char *array[6]; } record; int main() {  FILE *fp = fopen("test.csv", "r");  char line[LINE_LEN];  record data;  char *tmp;  int i;  while(fgets(line, LINE_LEN, fp) != NULL)  {   i = 0;   tmp = strtok(line, ",");   do   {    data.array[i] = tmp;    tmp = strtok(NULL, ",\n");    i ++;   } while(tmp != NULL);   if(i < 6) data.result = "×";   printf("項番:%s 名前:%s 身長:%s 体重:%s 血液型:%s 結果:%s\n",    data.param.num, data.param.name, data.param.height, data.param.weight, data.param.blood, data.param.result);  } } 一行ずつ読んではstrtok()で分解して表示、を繰り返しています。 複数のデータを持ちたい場合にはdataを配列にしましょう。 全て文字列で持っているので数値で扱いたい場合にはそれなりの対応が必要です。 また要素が4個以下(血液型がない場合)などのデータ自体の整合性は見ていないので不正データがあった場合には、このままではコケます。 なお、strtok()は「返すべきトークンが存在しない」状況になったらNULLを返します。 常に返り値チェックをして「返り値をチェックしないまま扱おうとしてAccess Violation」なんて事にならないように気をつけましょう。

回答No.5

「strtok()に苦戦している」とのことですが、「2,二郎,165.5,60,B,」の末尾がメモリにどのように展開されているかを知ることが strtok() をマスターするキーポイントではないでしょうか。 CSVファイルは fgets()を使って読み込みますが、読み込まれた「2,二郎,165.5,60,B,」の行の末尾の展開は 2,二郎,165.5,60,B,\n\0 となっています。 これをクリアするために strtok(NULL,",\n") とすると bus error が出てうまくいきません。 アドレスが \n を認知できず、読み込んだメモリ領域を超えてしまったことによる error と考えられます。 そこで \n の次は \0 にメモリ展開されていますから、strtok(NULL,"\0") とすると error は起こりません。 しかし、改行コードの \n が読み込まれるため、これを排除する新たな作業が必要となります。 *(ptr->judge+strlen(ptr->judge)-1)='\0'; ----- データファイル ----- 1,太郎,150,55.6,A,○ 2,二郎,165.5,60,B, 3,三郎,160.2,59.5,AB,○ #include <stdio.h> #include <string.h> //strtok(),strcpy() #include <stdlib.h> //exit(),atoi() #define SIZE 256 #define NUMBER 50 struct set1 { int code; char name[32]; float height; float weight; char blood[3]; char judge[3]; }; int main(int argc, char *argv[]) { struct set1 member[NUMBER], *ptr; char buffer[SIZE], work[12]; //作業用バッファ char *read_file; //読み込みファイル名 int lines; //読み込み行数 FILE *fp; int j; if (argc!=2){ printf("parameter error.\n"); exit(1); } read_file=argv[1]; if ((fp=fopen(read_file, "r"))==NULL){ printf("%s file can't open.\n",read_file); exit(1); } lines=0; while(fgets(buffer, SIZE, fp)!=NULL) { if(lines>= NUMBER){ printf("buffer overflow error!"); exit(1); } ptr = &member[lines]; ptr->code=atoi(strcpy(work,strtok(buffer,","))); strcpy(ptr->name,strtok(NULL,",")); ptr->height=atof(strcpy(work,strtok(NULL,","))); ptr->weight=atof(strcpy(work,strtok(NULL,","))); strcpy(ptr->blood,strtok(NULL,",")); strcpy(ptr->judge,strtok(NULL,"\0")); *(ptr->judge+strlen(ptr->judge)-1)='\0'; lines++; } fclose(fp); for (j=0; j<lines; j++) { ptr=&member[j]; printf("\t番号= %d, ", ptr->code); printf("名前= %s, ", ptr->name); printf("身長= %.1f, ", ptr->height); printf("体重= %.1f, ", ptr->weight); printf("血液型= %s, ", ptr->blood); printf("合否= %s\n", ptr->judge); } return 0; }

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.4

一箇所書き間違えました。 > 4.検索開始位置をカンマの次に移動して3に戻る、以下カンマが見つからなくなるまで繰り返し 4.検索開始位置をカンマの次に移動して”2”に戻る、以下カンマが見つからなくなるまで繰り返し です。

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.3

認識されている通りcsvのエレメント分割は単純にはいかない訳です。 #2で指摘されている部分の他にもエレメント内の"の扱いとかありますしね。 まぁ今回は「csvそのもの」ではなく「csvっぽいもの」として、ファイルフォーマットを以下に限定して考えることにしましょう。 ・要素に改行文字・ダブルクォーテーションは含まない(必ず1行でデータが完結する) ・空要素はある このとき、要素を取るためにはどうすればいいかという事を考える訳ですが、 1.1行読む(fgets()) 2.検索開始位置を行頭に置く 2.検索開始位置から最初にあるカンマ位置を取得する(strchr(), strcspn()など) 3.検索開始位置からカンマ位置までを切り出す(strncpy()など、終端処理を忘れずに) 4.検索開始位置をカンマの次に移動して3に戻る、以下カンマが見つからなくなるまで繰り返し 5.1に戻る、以下行が読めなくなるまで繰り返し という手順を踏むことになるでしょう。 本来のちゃんとしたcsvに対応するにはこれでは駄目ですが、質問の内容についてはこの手順で問題ないはずです。

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.2

すでに書かれているとおり、"%d"で読み込もうとしているから…です。 ","は数字ではありませんから、ココで止まるでしょうね。 "太郎"も"A"も"○"も数字ではありませんけどね…。 しかも例の場合CSVとして正しいのか… 二郎の行は 2,二郎,165.5,60,B, となるハズです。 1行読み込みして、strtok()でカンマで区切る。 なんてのがCSV読み込みでよくあるパターンですが……コレもアウトです。 # 「入門者向けの難易度とは言い難い」と言われる由縁。 なぜか初心者向けでCSV読み込みが例題に出されるパターンをよく見かけますが… 「ちゃんと」処理しようとした場合、どう見ても初心者向けではありません。 # データの入っていない場合は? データ中に","が含まれる場合は? データ中に改行が含まれている場合は? などなど。 以下、蛇足ですが… このコードはコンパイル通りますかね? >#define i 1000 >#define j 1000 >int d[i][j]; ここまではよいでしょう。 # iとかjをdefine定義することの是非は置いておきますが。 >for(i=0;i<=2;i++) >for(j=0;j<=6;j++) 定数をfor文のカウンタに使うのはいかがかと。 ちなみに、上のは… for(1000=0;1000<=2;1000++) for(1000=0;1000<=6;1000++) と同等になります。 # 1000=0と1000++が微妙。1000<=2は常に偽なので…最適化で丸ごと消える?? >fscanf(fp,"%d",d[i][j]); バッファオーバーランしてます。iとjは定数ですが…二次元配列dはそんな添え字は許可されません。 # まぁ、for文の条件からココには来ないハズですが。

aiue_7
質問者

お礼

いろいろとコメントありがとうございます。 ちょっと勉強して出直してきます。

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.1

scanf系の%dは数値がいない場合そこで解析を止めます。 また、「csvを読む標準関数」は存在しないのでいずれにせよその方法ではカンマで区切ることができません。 Cを始めて間もないのなら、まずは基礎を固めるのに時間をかけるべきです。 この課題自体は入門者向けの難易度とは言い難いので、例を提示すること自体は難しくはないのですが恐らくそちらが理解できないでしょう。

aiue_7
質問者

お礼

コメントありがとうございます。 文字と数値の入り混じったCSVファイルの読み方、また、各行内に空のセルがある場合 どうしたらいいのかわからなく投稿しました。 strtok関数とかも自分なりに使用してみたのですがなかなかうまくいかず、苦戦してます。 もう一度勉強してTRYしてみます。

関連するQ&A