• ベストアンサー

CでCSVファイルを扱う

いつもお世話になっております。 今回はCSVファイルの扱いについてお聞きしたいです。 現在、 "hoge","hogehoge","…","…" というようなCSVファイルを扱いたいと思い、いろいろ試しているのですがなかなかうまくいきません。 CSVの各行をgetlineで取得して、カンマで分けるだけならできたのですが、","のまとまりで分けたいのです。現在は文字で分割はできるのですが文字列で分割する方法がわかりません。 ","で分けたい理由はたとえば"hoge,hoge"という文字列をカンマで分けてしまうと二つに分かれてしまいそれを避けたいです。 分割点の前後の文字から判断しようともしてみましたがうまくいきませんでした。 アドバイスなどありましたらご教授願います。 現在は以下のようになっております。 ifstream ifs; string temp; LPCTSTR str; ifs.open("test.csv", ios::in); while(getline(ifs, temp)) {  str = strtok(const_cast<char*>(temp.c_str()), _T(",")); printf("%s\n", str);  while(str != NULL)  {   str = strtok(NULL, ",");   if (str != NULL)   {    printf("%s\n", str);   }  } } 以上のもので CSVの"1","2","3","4" が"1"と"2"と"3"と"4"になります。 これを1と2と3と4にしたいです。 よろしくお願いします。 環境はWindowsXP VisualStudio6.0 MFC未使用 です。お願いします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.6

  C だけど、参考になれば、 #include <stdio.h> #include <string.h> int main(void) { char str[] = "\"1\",\"2,2\",\"33,3\",\"444,4\",\"5555,5\"", *p = str + 1, *q; char buf[10][32]; int i, j; for(i = 0; q = strstr(p, "\",\""); i ++){ *q = '\0'; strcpy(buf[i], p); p = q + 3; } *strchr(p, '\"') = '\0'; strcpy(buf[i], p); for(j = 0; j <= i; j ++) puts(buf[j]); return 0; }  

iec1128
質問者

お礼

ご回答ありがとうございました。 まさにこのようなことがやりたかったです。 これを少しカスタムして完全に理想のものができました。 ありがとうございました。

その他の回答 (6)

  • yama5140
  • ベストアンサー率54% (136/250)
回答No.7

>ダブルクォーテーションに囲まれているカンマを無視するために三文字が連続(◆)しているときに分割したいということです。  No.6 さんのデータ例(*)をつかわせていただき、「ポインタ」を使わずに作ってみました。  ◆3文字目はチェックしていません・・、あしからず。    (BorlandC++5.6.4)  *:「ファイル」を想定し、\n を付加 #include <stdio.h> void main() {  char cBuf[256] = "\"1\",\"2,2\",\"33,3\",\"444,4\",\"5555,5\"\n";  int i, iTop; // CSVファイル オープン // while( NULL != fgets( cBuf, 256, fp ) ){   printf( "%s\n", cBuf );   for( iTop = i = 1; i < 256; i++ ){    if( '"' != cBuf[ i ] ) continue;    i++; // 連続して    if( '\n' == cBuf[ i ] ){ // 行末     cBuf[ i - 1 ] = 0;     printf( "%s\n", &cBuf[ iTop ] );     break; // 1レコード処理終了    }    if( ',' == cBuf[ i ] ){ // 「◆仕様」区切り     cBuf[ i - 1 ] = 0;     printf( "%s\n", &cBuf[ iTop ] );     i += 2; // 次のブロック     iTop = i;    }   } // } // CSVファイル クローズ } 注:インデントに全角空白を用いています。タブに一括変換して下さい。

iec1128
質問者

お礼

ご回答ありがとうございます。 こちらも試したところ、思ったことができるようです。 3文字目のチェックをしていないとのことですが、よく考えて、必要ない時はそうしたいと思います。

回答No.5

1文字ずつ調べるしかないと思います。 昔作ったプログラム例がありますので、よかったら参考にどうぞ。 http://jariten.dip.jp:8000/up/src/up0022.zip ダブルクォーテーションがない場合も、複数改行も対応しているつもりです。(Excelが吐くcsvにあわせました)

iec1128
質問者

お礼

ご回答ありがとうございました。 動作確認しました。思ったように動いてました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.4

strtokでは役不足です。 二重引用符の中には改行文字が含まれることもあるでしょうから、std::getlineでも不十分です。 結論としては、状態遷移を管理しながら一文字ずつ処理するか、yaccのようなパーサジェネレータを使うかです。 せめて、Visual C++ 6.0ではなく、Visual C++.NET 2003ぐらいであれば、Boost.Spiritを使えば簡単にできるのですが...

iec1128
質問者

補足

ご回答ありがとうございます。 ダブルクォーテーション内に改行が入る可能性は今回は無いです。 ですのでgetlineを使っています。 strtokで難しいとなると、地道に一文字ずつ見ていくしかないのでしょうか? 出来ればスマートにやりたかったのですが、一度地道にやってみます。

  • asuncion
  • ベストアンサー率33% (2126/6288)
回答No.3

> CSVの"1","2","3","4" が"1"と"2"と"3"と"4"になります。 > これを1と2と3と4にしたいです。 "1"という文字列ではなく、整数の1を得たいのですか? "hoge"という文字列からは何を得たいですか?

iec1128
質問者

補足

ご回答ありがとうございます。 説明不足であったため補足させていただきます。 例が悪かったです。たとえば "12,34","56,78","1111","2222" という文字列があったらカンマで分けると "12と34"と"56と78"と"1111"と"2222"の六つに分かれてしまいます。 今実現したいのはカンマで分けるのではなく <",">の三文字が連続しているときに分けたいということで上記例だと 12,34と56,78と1111と2222の四つに分割したいです。 ダブルクォーテーションに囲まれているカンマを無視するために三文字が連続しているときに分割したいということです。 わかりにくい説明かもしれませんがアドバイスお願いします。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.2

strtokの引数string2に渡すものを工夫してやればいいのでは? "自体は \でエスケープしてやれば文字リテラルに含められます "\"" で "1つの文字列を定義できます "\",\""で "," を渡せますよ

iec1128
質問者

補足

ご回答ありがとうございます。 アドバイスの点はすでに試してみましたが、出力を見ると <",">の三文字連続出現という意味ではなく "と,と"の各文字で分割するという意味でカンマだけの時より 細かく分かれてしまいました。

  • annyG
  • ベストアンサー率25% (10/39)
回答No.1

「"1"」を渡すと「1」が返ってくる関数を一つ作れば解決ではないでしょうか?

iec1128
質問者

補足

ご回答ありがとうございます。 No3の方の捕捉に書かせていただきましたが、例の書き方が悪く、混乱させてしまっているようですいません。 今困っているのは、ダブルクォーテーションの間にカンマが入ってしまうときの対処として<",">の三文字が連続しているとき分割したいというものでした。 わかりにくい説明ですがアドバイスがありましたらよろしくお願いします。

関連するQ&A

  • c#でC言語のstrtokに相当する関数は何か

    文字列から指定した文字でデータを区切る関数strtokがC言語にはある これに相当するC#の関数は何か 例えばCでは以下のように書く。 char data1[]= " 123 , 456 Yamada " ; char *token ; strtok( data, " ," ) ; /* スペースとカンマを区切りに文字列を抽出 */ token = strtok( str, " ." ); printf(" token chat = %s\n", token ) ; while ( token != NULL ) { token = strtok( NULL," ." ); if ( token != NULL ) printf(" token chat = %s\n", token ) ; } これに相当するc#のSplit関数を使用すると 不要な空白を取り出しているようである 知っている方がおりましたら、教えて下さい。

  • c言語でcsvファイルの処理で、処理速度が速いプログラムを書こうと思っ

    c言語でcsvファイルの処理で、処理速度が速いプログラムを書こうと思っています。 以下のようなcsvファイル、件数は約10000000件以上あるものを使います shop,ymd,gend,age,area,amt 20,2008-05-01,3,5,014,128 22,2008-05-01,2,4,015,350 : 二列目の日別、つまりymd別に最後列のamdの小計を出したいんですが、組んだプログラムを実行してみると、セグメンテーション違反ですと出てしまうんです。以下のようなプログラムを組んだんですが #include <stdio.h> #include <string.h> #include <time.h> #define MM 256 int main() { FILE *fp; char str[MM],*p1, *p2,*ymd; int num, sum; clock_t start,end; start = clock(); fp = fopen("csv.csv","r"); if(fp == NULL){ printf("ファイルが開けませんでした。\n"); return(0); } sum = 0; fgets(str, sizeof(str), fp); while(fgets(str, sizeof(str), fp) != NULL){ p1 = strtok(str, ","); p1 = strtok( NULL,","); ymd = p1; p2 = strrchr(str,','); sum[ymd] = atoi(p2+1); break; } while( fgets(str, sizeof(str), fp) != NULL && p1 != NULL){ p1 = strtok(str, ","); p1 = strtok( NULL,","); p2 = strrchr(str,','); if ( p2 != NULL ) { if(ymd == p1){ sum[ymd] += atoi(p2+1); }else{ printf("%s ,%d \n",ymd,sum[ymd]); strcpy(ymd,p1); } } } printf("%s ,%d \n",p1,sum); fclose(fp); end=clock(); printf("%.2f秒\n",(double)(end-start)/CLOCKS_PER_SEC); return(0); } うまくいきません。大体、処理速度は3秒以内を目指しています。 どなたかご教授御願いいたします。

  • csvファイルの実績データをC言語で解析するのですが...

    C言語を学び始めたばかりなのに、csvファイルの実績データでフィールドが15あり、レコード数が1000000近くあるファイルの15番目のフィールドを足し合わせて、出力するということをやっているのですが、まだまだわからないことだらけです。 1レコード目がカラム名なので2レコード目から足し合わせるんですがそこのところもよくわからずじまいで... 一応、書いたプログラムが #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { FILE *fp; char buffer[50],*p; int cnt, num, sum; fp = fopen("j0.csv","r"); if(fp == NULL){ printf("ファイルが開けませんでした。\n"); exit(-1); } while(fgets(buffer,fp) != NULL){ p = strtok(buffer,","); cnt = 1; while(p!=NULL){ num = atoi(p); printf("%d:%d,",cnt,num); p = strtok(NULL,","); cnt++; if(cnt==15) sum=sum+num } printf("\b\b \n"); } printf(%d \n",num); fclose(fp); return(0); } と書いたんですが、ぜんぜんな状態です。誰かご教授願えませんか?

  • 1000000レコードもあるcsvファイルの実績データをC言語で計算しているのですが...

    C言語を学び始めたばかりなのに、csvファイルの実績データでフィールドが15あり、レコード数が1000000近くあるファイルの15番目のフィールドを足し合わせて、出力するということをやっているのですが、まだまだわからないことだらけです。 一応、書いたプログラムが #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { FILE *fp; char buffer[50],*p; int cnt, num, sum; fp = fopen("j0.csv","r"); if(fp == NULL){ printf("ファイルが開けませんでした。\n"); exit(-1); } while(fgets(buffer,fp) != NULL){ p = strtok(buffer,","); cnt = 1; while(p!=NULL){ num = atoi(p); printf("%d:%d,",cnt,num); p = strtok(NULL,","); cnt++; if(cnt==15) sum=sum+num } printf("\b\b \n"); } printf(%d \n",num); fclose(fp); return(0); } と書いたんですが、ぜんぜんな状態です。誰かご教授願えませんか?

  • C言語でファイルの内容を strtok関数 を使って数字と文字を分けて

    C言語でファイルの内容を strtok関数 を使って数字と文字を分けて配列に格納したいのですが、うまくできません。 どこが駄目なのかご指摘をお願いします! ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int i=0; int num[10]; char na[10]; fp=fopen(argv[1],"r"); while(fgets(str,sizeof str,fp)!=NULL); tp = strtok ( str, " " ); while(tp != NULL ) { num[i]=atoi(tp); tp = strtok( NULL," "); if ( tp != NULL ){ na[i]=*tp; } i++; } printf("%d\n%s",num[0],na[0]); printf("%d\n%s",num[1],na[1]); fclose(fp); return 0; }

  • Borland C CSVファイル読み込み

    CSVファイルを読み込み、読み込んだ値で計算を行うプログラムを作っています。 ・環境はWindows VISTA UltimateでBorland C++ Compiler 5.5  ・CSVファイルのデータの形式は 1,4532 4,2131 6,4301 . . ・データ数は決まっていて今のところ全部で12個 そして以下のようにCSVの読み込みプログラムを試しに組んだ所、実行時エラーがでました。 #include <stdio.h> #include <stdlib.h> #include <string.h> void main(void) { int AN[11][1]; int i=0,j=0,c=0; char buff[1024], *tp; FILE *fp; /*配列初期化*/ for(i=0;i<11;i++) { for(j=0;j<2;j++) { AN[i][j]=0; } } fp=fopen("test1.csv","r"); if(fp==NULL) { /* オープン失敗 */ printf("ファイルがオープンできません\n"); exit(1); /* 強制終了 */ } while( fgets(buff, 1024 , fp) != NULL ) { tp=strtok(buff , ","); if (tp !=NULL) {AN[i][j] = atoi(tp);} printf("%d\n",AN[i][j]); tp = strtok(NULL , ","); if (tp !=NULL) {AN[i][j+1] = atoi(tp);} printf("%d\n",AN[i][j+1]); i++; } fclose(fp); } 実行するとファイルクローズの後、問題が発生したためプログラムを終了しましたと出ます。"AN[i][j]=atoi(tp)"の配列部分を単純に変数にするとこのようなエラーは出ないのですが。 なぜエラーが出るのか、どなたかご教授願います。

  • C言語 strtok

    失礼します。現在こちらでアドバイスを頂きfgetcを使用して配列に格納をすることができたのですが、CSVをカンマ区切りで格納したいのですが上手くいかず困っています。strtokを使用方法をドキュメントを読んでもうまく区切ったものを配列に入れる方法がわかりません 何卒よろしくお願いします。 ソースコード #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include<string.h> #define MAXITEM 1400 int split(char *str, const char *delim, char *outlist[]) { char *tk; int cnt = 0; tk = strtok(str, delim); while (tk != NULL && cnt < MAXITEM) { outlist[cnt++] = tk; tk = strtok(NULL, delim); } return cnt; } int main(void) { FILE *fp; char *fname = "testfile.csv"; char *tp; char *array[1400]; char *test[11][1400]; char c; int i = 0; int n,y; char *tp[1400]; fp = fopen(fname, "r"); if (fp == NULL) { printf("%sファイルが開けません¥n", fname); return -1; } while ((c = fgetc(fp)) != EOF) { array[i] = (char)c; i++; } tp = strtok(array, ","); puts(*tp); while (tp != NULL) { tp = strtok(NULL, ","); if (tp != NULL)puts(tp); } for (n = 0; n < 11; n++) { for (y = 0; y < 1400; y++) { test[n][y] = tp[y]; printf("%c", test[n][y]); } } fclose(fp); return 0; }

  • C言語に詳しい方お願い致しいます

    文字列Aから文字列Bを除いた結果を表示するプログラムなのですが 一ヶ所分からない部分があります。 #include <stdio.h> #include <string.h> int main(void){  char str1[] = "abcdefg";  char str2[] = "cde";  char str3[128] = "";  char *p1 = str1, *p2;  size_t len;  len = strlen(str2);  while((p2 = strstr(p1,str2)) != NULL) {   strncat(str3,p1,p2 - p1);   p1 = p2 + len;   printf("----\n");   printf("p2=%s\n", p2);   printf("str3=%s\n", str3);   printf("p1=%s\n", p1);  }  strcat(str3,p1);  printf("%s\n",str3);  return 0; } while文中の p1 = p2 + len; なのですが、何故これでp1の内容が「fg」になるのでしょう。

  • C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問

    C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問です! ファイルを開いた後でエラーとなるのですが、何が足りないのでしょうか? ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int k,i=0; int num[10]; char na[10][20]; fp=fopen(argv[1],"r"); if(fp==NULL){ printf("ファイルを開けません\n"); return 1; }else{ printf("開けた\n"); } while(fgets(str,sizeof str,fp)!=NULL){ tp=strtok(str," "); num[i]=atoi(tp); tp=strtok(NULL," "); strcpy(na[i],tp); i++; } printf("%d\n%s\n",num[0],na[0]); printf("%d\n%s\n",num[1],na[1]); fclose(fp); return 0; }

  • c言語 sscanf の書式についての質問です

    C言語でCSVファイルの読み込みをしようとしています。CSVの中身は以下の2通りあります。 ・[ ] で囲った文字列(1行に1つだけ) ・データの並び(1行に2つ以上) 具体的には、[ ] で囲った文字列と、最初の2つのデータのみが抽出対象です。そこで以下のコードを書いてみましたが、最後に示すように、[ ] で囲った文字列がうまく抽出できません。以下のコードのようなsscanfの使い方は間違っているのでしょうか?正しく動作するにはどうすればよいか、教えて下さい。 ---------- 以下、ソースコード #include <stdio.h> #include <string.h> int main(void) {  FILE *fp;  char *fname = "test.csv";  char s1[32], s2[16];  int ret;  char str[128];    /* csvファイルの一行 */  char structName[32];  /* セパレータ名 */  fp = fopen( fname, "r" );  if( fp == NULL ){   printf( "%sファイルが開けません\n", fname );   return -1;  }  while( fgets( str, 128, fp ) != NULL ){      /* セパレータ */   if( str[0] == '[' ){    printf( "str = %s\n", str );    ret = sscanf( str, "[%s]\n", structName );    printf( "structName = %s\n", structName );    continue;   }      ret = sscanf( str, "%[^,],%[^,]", s1, s2 );      printf( "s1 = %s\n", s1 );   printf( "s2 = %s\n", s2 );  }  fclose( fp );  return ret; } ---------- 以下、"test.csv"の中身 [ABC] test01,10,11,1.0,1.1 test02,20,21,2.0,2.1 test03,30,31,3.0,3.1 [DEF] test04,40,41,4.0,4.1 ---------- 以下、実行結果 str = [ABC] structName = ABC] s1 = test01 s2 = 10 s1 = test02 s2 = 20 s1 = test03 s2 = 30 str = [DEF] structName = DEF] s1 = test04 s2 = 40

専門家に質問してみよう