「これはヤバかったな」という遅刻エピソード

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

A 回答 (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に格納されているアドレスを代入しようとしているためエラーとなっています。

ひとつアドバイス。
どんなに短いプログラムでも、どこで何の処理を行うのかコメントは降っておいた方が理解しやすいと思う。

この回答への補足

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

補足日時:2009/11/13 13:42
    • good
    • 0

No.5


>ポインタの比較はできないってコンパイルできないので、
その際にエラーメッセージで行数は表示されませんでしたか?

>char str[MM],*p1, *p2,ymd1[20], ymd2[20];
と定義しているのに、
>ymd1 = p1;
のような使い方はできませんよ。

この回答への補足

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

補足日時:2009/11/13 08:56
    • good
    • 0

「セグメンテーション違反を直す」のは当然として, 「どこに時間がかかっているのか」をきちんと把握できていますか? #2 でも書かれていますが, 「どうしても必要な時間」より速くはできません.


ちなみに sum[ymd] ではコンパイルエラーにはならないです>#1. C では a[x] において「a と x の一方が整数, もう一方がポインタ」なら (その他いくつか条件はあるけど) OK です.

この回答への補足

限界を超えてまで早くしようとは思ってません。
目標は、目標として前回よりも早くなればいいんです。

あの、つまらない質問だと思うんですがポインタとポインタって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);
}

という風に変えたんですが、ポインタの比較はできないってコンパイルできないので、ポインタの比較ができないなら、別のアプローチを考えなきゃいけないんですが...

補足日時:2009/11/12 18:05
    • good
    • 0

3秒ですか


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

C/C++に連想配列はありません。

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

3秒の処理速度を実現するのは、難しそうでしょうねぇ。


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

この回答への補足

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

補足日時:2009/11/12 16:55
    • good
    • 0

>char str[MM],*p1, *p2,*ymd;


>int num, sum;
>sum[ymd] = atoi(p2+1);

sumの定義と使い方が食い違っています。コンパイルエラーは出なかったのですか?
char *型のymdを、配列の添字として使えるのですか?

この回答への補足

コンパイルでは、エラーが出なかったんです。
ただ、実行するとセグメンテーション違反です、とでるだけで。

sumの定義が違いますか...
C言語に触れるのも、結構久しぶりなので使い方を結構忘れていて、うろ覚えな感じで書き出したので。
最初から調べなおしてみたいと思います。

補足日時:2009/11/12 16:50
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています


おすすめ情報