• ベストアンサー

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秒以内を目指しています。 どなたかご教授御願いいたします。

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

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

No.6です。 >やっぱりポインタはイコールでつなげられないんですか。 ポインタ同士で代入(右辺のアドレスを左辺に入れる等)はできます。 まず、エラーの意味を理解してください。 簡単に書けば「型が違うから代入できない」と言っているのです。 No.6の -------- >char str[MM],*p1, *p2,ymd1[20], ymd2[20]; と定義しているのに、 >ymd1 = p1; のような使い方はできませんよ。 -------- について理解されていますでしょうか? >char str[MM],*p1, *p2,ymd1[20], ymd2[20]; をわかりやすいように分解します。 -------- char str[MM]; char *p1; char *p2; char ymd1[20]; char ymd2[20]; -------- p1、p2は「char型のポインタ」 str[]、ymd1[]、ymd2[]は「char型の配列」です。 >ymd1 = p1; は、ymd[0]に対してp1に格納されているアドレスを代入しようとしているためエラーとなっています。 ひとつアドバイス。 どんなに短いプログラムでも、どこで何の処理を行うのかコメントは降っておいた方が理解しやすいと思う。

yxia001
質問者

補足

有難う御座います。 コメントは今度から、振るように心がけたいと思います。 プログラムの直しも早速挑戦したいと思います

その他の回答 (6)

回答No.6

No.5 >ポインタの比較はできないってコンパイルできないので、 その際にエラーメッセージで行数は表示されませんでしたか? >char str[MM],*p1, *p2,ymd1[20], ymd2[20]; と定義しているのに、 >ymd1 = p1; のような使い方はできませんよ。

yxia001
質問者

補足

エラーの内容は test4.c: In function ‘main’: test4.c:27: error: incompatible types in assignment test4.c:36: error: incompatible types in assignment こう出ます。 やっぱりポインタはイコールでつなげられないんですか。 別の手法でいくしかないみたいですね。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

「セグメンテーション違反を直す」のは当然として, 「どこに時間がかかっているのか」をきちんと把握できていますか? #2 でも書かれていますが, 「どうしても必要な時間」より速くはできません. ちなみに sum[ymd] ではコンパイルエラーにはならないです>#1. C では a[x] において「a と x の一方が整数, もう一方がポインタ」なら (その他いくつか条件はあるけど) OK です.

yxia001
質問者

補足

限界を超えてまで早くしようとは思ってません。 目標は、目標として前回よりも早くなればいいんです。 あの、つまらない質問だと思うんですがポインタとポインタってstrcmpで比較する方法ってないんでしょうか? ちょっとスクリプトを変えたんですが #include <stdio.h> #include <string.h> #include <time.h> #define MM 256 int main() { FILE *fp; char str[MM],*p1, *p2,ymd1[20], ymd2[20]; int num, sum=0; clock_t start,end; start = clock(); fp = fopen("/data/testdata/journal/j080240.csv","r"); if(fp == NULL){ printf("ファイルが開けませんでした。\n"); return(0); } num=1; fgets(str, sizeof(str), fp); while(fgets(str, sizeof(str), fp) != NULL && num==1){ p1 = strtok(str, ","); p1 = strtok( NULL,","); ymd1 = p1; p2 = strrchr(str,','); sum = atoi(p2+1); num = 2; } while( fgets(str, sizeof(str), fp) != NULL && p1 != NULL){ p1 = strtok(str, ","); p1 = strtok(NULL,","); p2 = strrchr(str,','); ymd2 = p1; if ( p2 != NULL ) { if(strcmp(ymd1,ymd2) != 0){ printf("%s ,%d \n",ymd1,sum); strcpy(ymd1,ymd2); }else{ sum += atoi(p2+1); } } } printf("%s ,%d \n",ymd2,sum); fclose(fp); end=clock(); printf("%.2f秒\n",(double)(end-start)/CLOCKS_PER_SEC); return(0); } という風に変えたんですが、ポインタの比較はできないってコンパイルできないので、ポインタの比較ができないなら、別のアプローチを考えなきゃいけないんですが...

  • php504
  • ベストアンサー率42% (926/2160)
回答No.4

3秒ですか メモリに余裕があるのならファイルを全部メモリに読み込んでから処理すれば少しは速くなるかも あと日付が必ず同一日は連続しているとかいう保証があればプログラムも楽になりそうですが

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

C/C++に連想配列はありません。自作するなり、C++でstlなどを使います。 あと、文字列の扱いがぜんぜんなっていません。 全体的なアルゴリズムも間違っています。

  • hidebun
  • ベストアンサー率50% (92/181)
回答No.2

3秒の処理速度を実現するのは、難しそうでしょうねぇ。 対象ファイルのサイズは、200~300MBぐらいはあるのではないでしょうか? 読み出しメディアのスペックが、例えば40MB/secなら、 どれだけプログラムが速くても、5秒以上かかりますよ。

yxia001
質問者

補足

そうなんですか。 前に、むちゃくちゃに書いたスクリプトで実行したら、15秒以上かかってしまって。 これでは、使い物にならないとの事だったので、さらに早くしようと書き直している最中なんです。

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

>char str[MM],*p1, *p2,*ymd; >int num, sum; >sum[ymd] = atoi(p2+1); sumの定義と使い方が食い違っています。コンパイルエラーは出なかったのですか? char *型のymdを、配列の添字として使えるのですか?

yxia001
質問者

補足

コンパイルでは、エラーが出なかったんです。 ただ、実行するとセグメンテーション違反です、とでるだけで。 sumの定義が違いますか... C言語に触れるのも、結構久しぶりなので使い方を結構忘れていて、うろ覚えな感じで書き出したので。 最初から調べなおしてみたいと思います。

関連するQ&A

  • C言語でcsvファイルの日別の金額を足し合たいんですが..

    csvファイルのフィールドが15あるうちの2番目に日にち、15番目に金額が書いてあり、日にちには同じ日がいくつもあるので、その金額をそれぞれ足し合わせたものを出力するというものをやろうとしているのですが、C初心者何ですけど一応書いたプログラムが #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> int main(void) { char buffer1[512], buffer2[512],*p,*tp; FILE *fp; int i,j,num,sum; clock_t start,end; start = clock(); fp=fopen("j0.csv","r"); if(fp == NULL){ printf("ファイルが開けませんでした。\n"); exit(-1); } sum=0; while(fgets(buffer1,256,fp)!=NULL && fgets(buffer2,256,fp)!=NULL){ tp = strchr(buffer1, ','); p = strchr(buffer2, ','); tp = strchr(p+1, ','); for(j=0;j<13;j++) p = strchr(p+1, ','); num=atoi(p+1); sum+=num; if(tp==tp+1); sum+=atoi(p+1); else{ printf("%cでは%d円であった。\n",tp,sum); sum=0; } } return 0; } こんな感じなんですが、どうかご指導御願いします。

  • 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言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問

    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言語でファイルの内容を 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言語、ファイル操作、fgets()について

    次のプログラムは入力された行を読み込み、コマンドラインで指定されたファイルに書き込みます。 空白行が入力されたら、入力の終了とみなしてファイルを閉じます。続いてファイルを入力用に開き、 fgets()を使ってファイルの内容を表示するものです。 (ソースコードが長くてすみません) #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { FILE *fp; char str[80]; /* コマンドライン引数を検査する */ if(argc!=2) { printf("ファイル名を指定してください\n"); exit(1); } /* 出力用にファイルを開く */ if((fp = fopen(argv[1], "w"))==NULL) { printf("ファイルを開くことができません\n"); exit(1); } printf("終了するには空白行を入力してください\n"); do { printf(": "); gets(str); strcat(str, "\n"); /* 改行を追加する */ if(*str != '\n') fputs(str, fp); } while(*str != '\n'); fclose(fp); /* 入力用にファイルを開く */ if((fp = fopen(argv[1], "r"))==NULL) { printf("ファイルを開くことができません\n"); exit(1); } /* ファイルを読み込み直す */ do { fgets(str, 79, fp); if(!feof(fp)) printf(str); } while(!feof(fp)); fclose(fp); return 0; } 【質問】fgets()内のint型の数値「79」がどうして79なのかが分かりません。     80でも良いような気がするのですが・・・

  • 複数のファイルへの一括処理(困ってます)

    あるディレクトリ内の全てのファイルに同じ処理(添付のプログラムによるtest1.csvファイルに対する処理と同一の処理)をしたいと考えています。 どのようなプログラムを組めば良いのですか? 手法については検討もつかない状況です。 教えていただけないでしょうか? よろしくお願いいたします。 (1)添付のプログラム #include <stdio.h> #include <conio.h> #include <string.h> #include <stdlib.h> int main () { FILE *fp; char a[50]; double b; char *p; if((fp = fopen("test1.csv", "r")) == NULL) return 0; while(fscanf(fp, "%s\n", &a) != EOF){ p = strtok( a, "," ); b = atof(a); printf("%lf ", b); while(p != NULL) { p=strtok(NULL, ","); if(p != NULL) { b = atof(p); printf("%lf\n", b); } getch(); } } fclose(fp); return 0; } (2)プログラムで使うCSVファイル -749.95,1.499894061 -749.9,2.371277071 -749.85,3.01070131 -749.8,-0.92298313 -749.75,3.111961134 -749.7,-1.642278763 -749.65,2.759562723 -749.6,0.869387239 -749.55,1.400240703 -749.5,-0.574988131 -749.45,-2.282041367 -749.4,-1.104953718 -749.35,2.45568825 -749.3,-1.391859875 -749.25,-1.600438736 -749.2,0.116074589 -749.15,2.973864166 -749.1,2.652719198 -749.05,1.584699094 -749,2.129474673 -748.95,1.847275561 -748.9,2.974398019 -748.85,1.561458246 -748.8,2.897905731 -748.75,2.655075544 -748.7,-2.677451989 -748.65,-0.871814301 -748.6,-3.098832888 -748.55,2.565297638 -748.5,-2.073626148 -748.45,-0.300440285 -748.4,0.515248274 -748.35,2.983715685 -748.3,-2.47909508

  • 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未使用 です。お願いします。

  • いつもお世話になっております。http://oshiete.goo.n

    いつもお世話になっております。http://okwave.jp/qa/q5836517.htmlで質問させているものです。 皆さんのアドバイスを頂き、2分探索法で郵便番号から住所を検索するプログラムが出来たのですが、 住所から郵便番号を2分探索法で出すプログラムも同じ方法でやろうとしましたが、比較対象が漢字の為、大きい・小さいの判断できずに上手くプログラムが出来ていません。 csvファイルは読みデータをひとつに繋げてあいうえお順にソートしました プログラムを一部載せておきます(かなり省略済みですが…) #define NAME ken_all_address.csv int main(int argc,char *argv[]) { struct tb line; FILE *fp; char buff[SIZE], string_buff[SIZE]; char *address,*ret; int flag,linesu,linesu1,sum,count,up,up1,low,low1,center,center1; int i,j; long pos[FSIZE]; clock_t start,end; start = clock(); //引数処理 if((fq=fopen(NAME1,"r")) == NULL){ printf("ファイル%sが開けません\n",NAME1); return -1; } if((fp=fopen(NAME,"r")) == NULL){ printf("ファイル%sが開けません\n",NAME); return -1; } flag = 0; address = argv[1]; count=0; sum=0; if(atoi(address) == 0){ for(i=0; ;i++){ pos[i] = ftell(fq); ret=fgets( buff, sizeof(buff), fp ); if(ret==NULL){ break; } } linesu = i; //printf("%d",linesu); low=0; up=linesu-1; while(low <= up){ center=(up+low)/2; fseek(fq,pos[center-1],SEEK_SET); fgets( buff, sizeof(buff), fp ); strtok(buff,",\""); strtok(NULL,",\""); strcpy(line.now_num,strtok(NULL,",\"")); strtok(NULL,",\""); strtok(NULL,",\""); strtok(NULL,",\""); strcpy(line.kanji1,strtok(NULL,",\"")); strcpy(line.kanji2,strtok(NULL,",\"")); strcpy(line.kanji3,strtok(NULL,",\"")); strcpy(string_buff,line.kanji1); strcat(string_buff,line.kanji2); strcat(string_buff,line.kanji3); printf("%s %s %s\n",line.kanji1,line.kanji2,line.kanji3); if(strcmp(string_buff,address)==0){ printf("〒%s \n",line.now_num); flag=1; } if(strstr(string_buff,address) ==NULL){ low=center+1; } else{ up=center-1; } } } fclose(fp); if(flag==0 && atoi(argv[1]) == 0){ printf("「%s」に該当する郵便番号はありませんでした\n",address); } if(flag==0 && atoi(argv[1]) != 0){ printf("「%s」に該当する住所はありませんでした\n",address); } end = clock(); printf("引数=%s\n",address); printf("%.30f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC); printf("fgetsの実行回数=%d回\n",sum); printf("比較回数=%d回\n",count); printf("\n"); return 0; }