• ベストアンサー

大規模データの処理について困っています

掲題の通り、大規模データの処理で悩んでおります。 行ベクトル150万、列ベクトル14のCSVファイルを読み込もうとしているのですが、データ数が10万以上になるとVisual C++が勝手に動作を停止してしまいプログラムを実行することができません。 具体的には、CSVファイル上の4列目に記載されている都道府県名のデータを配列で取り、画面に表示しようと、次のようなプログラムを書いているのですが、 #include<stdio.h> #include<string.h> #include<stdlib.h> #define FNAME "data.csv" #define NUM 10000 int main(void) { FILE *fp; char buf[256]; char *p_token; char dat[14][100]; int n; int i; long int j; char *place[NUM][15]; place[NUM][15]=(char*)malloc(sizeof(char)*NUM); fp = fopen(FNAME,"r"); if (fp == NULL) { printf("ファイルをオープンできませんでした\n"); return 0; } for(j=0;j<=NUM;j++){ fgets(buf,256,fp) !=NULL; p_token = strtok(buf, ","); strcpy(dat[0],p_token); n=1; while(-1) { p_token = strtok(NULL,","); if(p_token == NULL) { break; } strcpy(dat[n],p_token); n++; } if(j!=0) { place[j-1][10]=dat[3]; printf("%s \n",place[j-1][10]); } } fclose(fp); free(place[NUM][15]); return 0; } NUMの数を10万以上にすると、実行してもプログラムが勝手に停止してしまいます。書籍もネットも大分読み漁ったのですが、処置がまったくわからず途方にくれています。 どなたかこうした処理に詳しい方、アドバイスをいただけないでしょうか。よろしくお願い申し上げます。 追記:(1)都道府県名を二次元配列で取っているのは、都道府県名が「大阪府」などと、CSVファイル上で日本語で記載されているからです。 (2)プログラムを実行する際にデータ数を10万以上にすると、CSVファイルをフォルダ内においていなくてもプログラムが停止します(ただしコンパイルエラーはでません)。つまり、メモリの確保に問題があるということになるのでしょうか?

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

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

こんばんは. メモリの制限について気にしておいでのようですが, それより先に気にすることがあると思います. 何をしたいかはともかくとして, > char *place[NUM][15]; の部分はかなり問題です. この場合はmain関数なので救いがありますが, このような巨大な変数を自動変数として確保してはいけません. 通常,自動変数はスタック領域に取られるからです. スタック領域はせいぜい数MBですから,それだけで領域を食い潰してしまいます. 大きな変数は必ずヒープ領域から取らなければなりません (用語の意味が分からない場合はご自身で調べてください,とても基礎的な内容です). また,全てのデータを「そのまま」メモリ上に展開する必要があるのでしょうか? 日本語文字列をそのまま展開するより数値なりアルファベットなりに置き換えた方が良いように思えます. (1)エントリを取得 (2)対応表を探索 (2a)存在すれば値に置き換える (2b)存在しなければ新たな値を定義して置き換える (3)数値に置き換えた行ベクタをメモリに保存 とすれば,後々ソートもやりやすいかと思いますが. 都道府県名は47しかないので,途中で対応表を操作せずに済むので非常に楽です. そして,ソートして出力する段で改めて対応表に基づき,値を文字列に直せば良いと思います.

em072010
質問者

お礼

ご回答のほど、ありがとうございます! 静的記憶でとるべく配列を宣言する場所をmain関数の外に出したらあっさりできました。本当にありがとうございます!! つまり、スタックオーバーフローが生じているということだったんですね。やっとエラーの意味が理解できました。 後半の指摘も言われてみればその通りですね(汗)。 全部数字などに置き換えた方が扱いも楽ですし、折角の連休ですし、がんばってやってみたいと思います。 この度はご親切に色々教えて下さり、本当にありがとうございました。

その他の回答 (5)

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

> fclose(fp); > free(place[NUM][15]); この2行の間で printf( "--- 確認 ---\n" ); printf( "%s\n", place[0][10] ); printf( "%s\n", place[1][10] ); を実行すると面白いかも ・・・

em072010
質問者

お礼

ご回答いただきありがとうございます。 一応やってみましたが、普通に都道府県名が表示されるだけでしたよ。

  • Interest
  • ベストアンサー率31% (207/659)
回答No.5

ANo.3 = Interest です。 ANo.4のお礼で解決できたかのようなことを書かれていますが、本質的には何も解決してないんじゃありませんか? > 最終的に何をしようとしているかといいますと、CSV形式のデータから > 全体を読み込み、商品名と時系列によってソートをかけ全体のデータ > を行ベクトルごとに並べ替えることを目的としています。 というのが本来の目的なのですね。 > ただ、実は今回のデータ分析は研究目的で行っておりまして、 > 将来的にはさらに大規模なデータを処理する可能性があるからということで、 > 教授に他のソフトウェアを使うのを禁じられています。 でしたら、扱うデータの量が膨大なのですから、処理速度や今後の応用のしやすさも含めて、DBMSをつかうのがベストな選択肢だと思います。なにせ、DBMSはそういう膨大なデータを並べ替えたり、ある条件下のものだけ抽出したりするためのソフトウェアなのですから。 例えば Microsoft Office Access 2007 アカデミック版なら \15,000-くらいで買えますし、CSVファイルをAccessに読み込む(インポートする)ことも簡単です。Accessと簡単な入門本を買ってきたら、PCにインストールしてからCSVファイルをインポートして簡単な並べ替えができるようになるまでに、2時間もかからずに済むんじゃないでしょうか。 実際に大量のデータ解析をしようとすると、入力元のデータにゴミが混じっていたりするので、ごみを取り除く作業が結構手間だったりしますが。 > 何とか教授に怒られないようにがんばりたいと思います。 ちょっとばかり追い詰めれられた感がありますね。指導教官(や上司)の顔色をうかがうようになったらやばい証拠です。「がんばる」のではなく、どうやったら最短の手数で最大の効果を得られるか考える方に頭を使いましょう。 PS: malloc を使ってメモリを確保するときは、ファイルポインタを使うときと同様、mallocの戻り値がNULLになっていないかどうか確認しましょう。(NULLならメモリ確保に失敗しています。)

em072010
質問者

お礼

ご回答ありがとうございます。 DBMSってそんなに高いものではないのですね。 てっきりものすごく高価なプログラムなのかと思っていたのですが...。ちょっくらヨドバシにでも行ってきます。 後半の指摘、ごもっともです。今思うと、最近茫然自失としている時があったり朝目覚める時間が普段より遅かったりペースがすっかり乱されていました。 まあ、あまり自分を追い込まずうまくやりたいと思います。

  • Interest
  • ベストアンサー率31% (207/659)
回答No.3

> メモリの確保に問題があるということになるのでしょうか? 直接的にはそのとおり、メモリの確保に問題があります。特に、 (1) 文字列の扱い (2) 配列とポインタ に関して何か誤解されているようです。 > 都道府県名を二次元配列で取っているのは、都道府県名が「大阪府」などと、CSVファイル上で日本語で記載されているからです。 Windows上で日本語を扱うときは、charではなくて、.NET使ってよいなら string型を使うか、従来通りなら TCHAR 型を使うのが定石だと思います。 「文字コード」という概念をご存知でなければ、Unicode、Shift JIS、EUC-JPなどをキーワードに検索してみてください。 参考: http://www.usefullcode.net/2006/11/tcharlpctstrlptstr.html 続いて、 > char *place[NUM][15]; > place[NUM][15]=(char*)malloc(sizeof(char)*NUM); これがもう滅茶苦茶。 そんなことをするより、最初の目的が > CSVファイル上の4列目に記載されている都道府県名のデータを配列で取り、画面に表示 でしたら、150万行も都道府県名を画面上で見るど考えられないので、重複する都道府県名は無視すると仮定して、次のようなプログラムにすれば巨大なメモリを確保する必要もなくなるものと思います。 (1) CSVファイルから1行読み取る。 (2) 4列目から都道府県名を取り出す。 (3) (2)で取り出した取り出した都道府県名が、読み込み済み都道府県名のリストにあるか検索する。   すでにある場合、読み捨てる。   まだない場合、読み込み済み都道府県名のリストに追加する。 (4) 全ての行を読み終えるまで、(1)~(3)を繰り返す。   全ての行を読み終えたら、(5)に進む。 (5) 読み込み済み都道府県名のリストを画面に表示する。 上記の(3)は、最大150万×47回の文字列比較をすると処理時間が膨大になりますから、処理速度を向上するためにはちょっとした工夫が必要です。 以下、独り言。 AccessやSQL ServerみたいなDBMS使えるなら、DBMSに食わせて吐き出させた方がいろいろと応用が利いていいかも。 私ならExcel使って4行目に対してフィルタかければ、ソースコードを1行も書かずに終わり。

em072010
質問者

お礼

事細かにご回答いただき、誠にありがとうございます。 文字コードの件、参照していただいたURLが非常に参考になりました。 漠然とは知っていたつもりだったのですが、今となっては自分の不勉強さを恥じる気持ちで一杯です。もっと勉強しなければいけませんね。 さて、なぜ都道府県名を全部表示しようとしているかとのことなのですが、最終的に何をしようとしているかといいますと、CSV形式のデータから全体を読み込み、商品名と時系列によってソートをかけ全体のデータを行ベクトルごとに並べ替えることを目的としています(もとはばらばらに並んでいるものを下のような感じにしたいです)。 19991001 vodka & cavier ...(その他の列ベクトル) 19991002 vodka % cavier ... ... 19991001 wine coldvier ... 19991002 wine coldvier ... ただ、今回質問させていただくにあたりまして、プログラムがあまりに長くても仕方がないということで、一番引っかかってた都道府県名という部分だけを抜き出して、「なぜ全体を読み込めないのか」という質問に変えて掲示させていただきました。 説明不足であったことを深くお詫び申し上げます。 それを踏まえますと、ご回答いただきました(1)~(5)のステップは商品名ごとにソートする際に非常に役に立つと思います。特に、都道府県名と違って商品につきましては何種類含まれているのかわからないもので、こうやるしかないですよね...。 またExcelの件、その通りなんですよね。 ただ、実は今回のデータ分析は研究目的で行っておりまして、将来的にはさらに大規模なデータを処理する可能性があるからということで、教授に他のソフトウェアを使うのを禁じられています。そのため、これまで使ったことのないC++でなんとかしようとしているのですが、徹夜続きでもう嫌になっています...。 と、愚痴ってもしかたないですね(笑)。 何とか教授に怒られないようにがんばりたいと思います。 最後になりましたが、親切に説明していただき、本当にありがとうございました。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.2

> char *place[NUM][15]; > place[NUM][15]=(char*)malloc(sizeof(char)*NUM); たぶんこの辺でとんでもない勘違いをしているかと。 何をしようとしていますか?

em072010
質問者

補足

ご回答いただき、本当にありがとうございます。 該当箇所ですが、上段は「文字列の配列は二次元でとれ」と本に書いてあったのでその通りにしてみました。 それで下段なのですが、その分のメモリを確保しなければいけないと思い書いた文なのですが、この書き方がまずいのでしょうか?

noname#96023
noname#96023
回答No.1

ぱっと見で、NUMが1万の時に1.5GBのメモリを確保 NUMが10万の時に150GBのメモリを確保しているように思えますが。。。 とりあえずNUMが1万のときのメモリ使用量をタスクマネージャで見てみては

em072010
質問者

お礼

早速ご回答いただき、誠にありがとうございます。 一応タスクマネージャを確認したのですが、そんなにやばいことにはなっていませんでした。 回答をまた質問で返すようで申し訳ないのですが、その後インターネットで色々調べていた所、Windowsの32bit版であったりVisual C++のExpress Editionであったりすると、そもそもメモリの使用量が制限されるとの情報に出くわしたのですが、そういうことって本当にあるのでしょうか? お恥ずかしながらどちらにも該当しているもので...。

関連するQ&A

専門家に質問してみよう