• ベストアンサー

1行ずつではなくまとめてファイル出力したいのですが

Cは初心者です。というかプログラム自体ずぶの素人です。 パラメーター1パラメーター2パラメーター3を使って計算をし、結果として パラメーター1,パラメーター2,パラメーター3,数値2,数値3,数値4,数値5,数値6,数値7 というような形式で1行ファイル出力をし、 パラメーターを変えて(for文)またファイル出力をし、とかなりの回数繰り返す計算をしております。 (元々AWKで動いていた自作プログラムの移植です) 現在、 #include <fstream> ofstream file1("c:\\data.csv",ios_base::app |ios_base::out) ; file1 << parameter1 << "," << parameter2 << ~中略~ << "," << value1 << "," << value2 << "," << ~中略~ "\n" ; のようにファイル出力しているのですが、ファイルサイズがでかくなってくると、もの凄く遅くなります。 ひょっとして、ファイルオープン→ファイル末尾まで開く→1行書き込む→ファイルクローズ→ の繰り返しで、だからファイルが大きくなると遅くなるのではないかという気がしているのですが、 何行も(1000行とか1万行とか5万行とか)一気に出力する方法はないでしょうか? まず、どうやってデータを溜め込み、次にどういう出力方法があるのか、教えていただけないでしょうか。 parameter1~Value7(本当は倍以上ありますが)をそれぞれ全て配列にし、ファイルをオープンして閉じずにfor文で書き込む、なんてのも考えたのですが、どうしたら良いか判らずギブアップです。 もうちょっとスマートに、file1のところを配列にするとか、file1にどんどんデータを書き加えて巨大な一文にしてしまうとか、何か方法はないでしょうか? なお、cはマイクロソフトが只で配っているVisual Studio の VisualC++ Express Editionを、よく判らないまま使用しております。 先週ダウンロードしましたので、おそらく最新版だと思います。 ポインタの辺りは、理屈は解りましたがスラスラ使いこなせるということはなく、他人のソースを見てもよく判らない状態です。 というか、どこまで書き換えて良い文字で、どこまでが標準関数(?)等の書き換えられない文字かもよく解っていないレベルです。 よろしくお願いいたします。

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

  • ベストアンサー
  • zwi
  • ベストアンサー率56% (730/1282)
回答No.3

やたらと、オープンクローズを繰り返すのは、 ofstream file1("c:\\datawax2.csv",ios_base::app |ios_base::out) ; がif文の中に書かれているからです。 ofstream は宣言が有効範囲外(この場合if文の{}の外)に出ると無効になりファイルをクローズしてしまいます。 mainの最初で宣言すれば、オープンを続けた状態になります。 途中でクローズさせたければ、CTRL+Cを自分でキャッチしてクローズ終了させれば問題ないと思います。 fopenを使ってもストリーム処理であることには違いありませんから、強制終了時に出力が終了していない可能性があります。つまり、ofstream と変わりません。

tekcycle
質問者

お礼

ありがとうございます。 虫取りも終わり、現在無事に動いております。 AWKの300倍以上の速度で。 なるほど、そういうことがあるのですね。 うーん、自分で勉強していてそんな情報に行き着くにはどれだけかかったことか。 どうもありがとうございました。

その他の回答 (3)

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.4

★アドバイス >教材は、c++よりもcなんでしょうか? >c++だと、クラスだのストリームだのMFCだのがいっぱい書かれている割に >こういう事が書かれていない物が多そうなのですが。  ↑  これは一通りC言語を知っている人用に書かれている場合ですね。  私が最初に読んだ C++ の入門書も C 言語を既に知っている前提で解説していましたから。  良く分からないまま C++ を使うよりかは C 言語の基礎から始めた方が良いでしょう。 >なお、最初にfopenして、プログラム終了までfcloseしないものは、 >運用上拙いのかもしれません。  ↑  なぜ?そう思うの? >丸一日丸何日動かすので、度々クローズしてくれると…その時途中までの結果が残せると思うのですが。  ↑  この場合は書き込み中の時に明示的に fflush() 関数を呼んであげれば良い。  これで出力ストリームのバッファをファイルに必ず書き込みます。  これを利用すれば fopen~処理~fclose をずっと行っても途中までの結果を残せます。  http://www.bohyoh.com/CandCPP/C/Library/fflush.html→『fflush』  『fflush』関数はデータを100行出力したらフラッシュするようにすれば良いでしょう。 ・下にサンプルを載せておきます。 サンプル: int main( int argc, char *argv[] ) {  FILE *fp;    if ( (fp = fopen("c:\\kekka.csv","a")) != NULL ){   int array_output[ 3100 ][ 12 ];   int count = 0;      for ( int i2=i-3000+1 ; i2<=i ; i2++ ){    if ( ++count >= 100 ){     fflush( fp );     count = 0;    }    fprintf( fp, "%s,%s,%s,.....\n", array_output[i2][1], array_output[i2][2], array_output[i2][3], … );   }   fclose( fp );  }  return 0; } その他: ・上記のように最初に fopen して計算結果を fprintf で出力して最後に fclose します。  途中でデータを 100 行出力したら明示的に出力バッファをファイルに書き出す指示を  fflush 関数で行っています。1行でデータが長い場合は 10 行単位で fflush したり、  あるいは fprintf の次に必ず fflush を記述します。ただしこの場合は出力処理が  長くなります。よって適度な行数を出力したら fflush を呼ぶようにします。 >クローズしない場合、途中で停止させた場合、どこまで残るんでしょう。  ↑  下の『参考URL』にバッファを大きく取り最後に fflush しています。  ここを読んでみて下さい。ストリーム・バッファの動きが分かります。 ・以上。

参考URL:
http://www9.plala.or.jp/sgwr-t/lib/fflush.html
tekcycle
質問者

お礼

フラッシュフラッシュというのが何なのかようやく理解できました。 > if ( ++count >= 100 ){ なるほど、こう書くのですね。if括弧内のどこかにcount++を埋め込んでいました。こりゃ楽ですね。勉強になります。 c++の入門本を読んだのですが、クラスだのポインタだのはよく書かれていたのですが、標準関数に対してほぼノータッチで。(良い本だとは思います) もう一冊の初中級者向けと思われる本にも、C++らしい事は載っていても、標準関数はあるかないか程度で。 本棚に埋もれていた、かなり前に何となく古本で買ってしまったTurboC(懐かしい)の本を見つけ、そこに標準関数が触りくらい載っていて、少し書けました。 もっとも今となっては古い物もあるようで...。 Cで良いそうですので、少々古い物でよいのなら近所の大学の図書館にあるでしょうから、近々行ってみます。 どうもありがとうございました。

  • zwi
  • ベストアンサー率56% (730/1282)
回答No.2

複数行のcsvファイルを作りたいって認識でよいですか? 遅くなっているのは、ファイルのオープンクローズを繰り返しているからだと思います。 次のような手順でやれば早くなるはずです。 (1)ファイルオープン (2)forループ (3)計算 (4)file出力 (5)forループの終わり (6)ファイルクローズ 一度ソースを見せてもらえると的確に答えられるのですが。

tekcycle
質問者

お礼

ありがとうございます。 補足の方が長く懸けるようですので補足に書いておきました。 よろしくお願いいたします。

tekcycle
質問者

補足

#include "stdafx.h" #include <iostream> #include <fstream> #include <sstream> #include <string> #include <time.h> using namespace std; ~~各種グローバル変数の設定(大概グローバルにしています)~~ ~~各種関数の宣言(今のところ7つくらい)~~ ~~.csvファイルを読み込むためのクラスの宣言~~ void main(int argc,int *argv){ ~~コマンドライン引数の処理と時刻表示~~ ~~各パラメーターの値がそれぞれいくつのときは何番目の処理なのかを設定する~~ iは順番、array_parameter1[i],array_parameter2[i],array_parameter3[i],... ~~データファイル(.csv)を複数読み込む~~ ~~上で決めた順に計算開始~~ for (i=1;i<=10000;i++){ //ここでは1と10000ですが、これはそれぞれコマンドライン引数にするつもりです。 //array_parameter1[i]、array_parameter2[i]等を使って計算させる。 calculate() ; //関数に飛んで処理。Value1、Value2、Value3....などを得る。ただしグローバル変数。 ~~ここからが懸案なのですが...~~ ostringstream filetemp ; filetemp << array_parameter1[i] << "," << array_parameter2[i] << "," << array_parameter3[i] << "," << Value1 << "," << Value2 << "," << Value3 << "," << i <<"\n" ; string str=filetemp.str() ; if (ii%3000==0){ ofstream file1("c:\\datawax2.csv",ios_base::app |ios_base::out) ; file1 << str ; } ~~書き出すか別の変数に収めたらValue1、Value2、Value3は初期状態に戻しておく~~ } //for文終わり ~~後は時刻表示や毎時何回転したか、等のどうでも良い表示と、まだ書きだしてない物を最後に吐き出して終わり。~~ return ; } void calculate(){ ~~後略~~ の様な感じなんですが。これでよろしいでしょうか? ostringstreamでどうにか溜められないかと思ったのですが(それも無理なようですが)、お話からするとofstreamを使っている時点で無理なようですね。 すると、fopenしてfprintfでfcloseでしょうか? 教材は、c++よりもcなんでしょうか?c++だと、クラスだのストリームだのMFCだのがいっぱい書かれている割にこういう事が書かれていない物が多そうなのですが。 なお、最初にfopenして、プログラム終了までfcloseしないものは、運用上拙いのかもしれません。 丸一日丸何日動かすので、度々クローズしてくれるとctrl+cでプログラム自体停止できて、その時途中までの結果が残せると思うのですが。 クローズしない場合、途中で停止させた場合、どこまで残るんでしょう。 半端な行があっても停止の数行前まで大体残ってくれるなら(そしてファイルが壊れていないなら)最初にfopenして最後にfcloseでも良いのですが。 この辺りどうなのでしょうか? よく判らないまま手元の本をアレンジすると、 void print_out(void){ FILE *fp int array_output[3100][12] ; if ((fp=fopen("c:\\kekka.csv","a"))==NULL){ printf("ファイルが開けません\n") ; exot(1) ; } for (int i2=i-3000+1 ; i2<=i ; i2++){ int fprintf (fp,"%s,%s,%s,.....\n",array_output[i2][1],array_output[i2][2],array_output[i2][3],.....) } fclose(fp) ; } こんな感じでどうなのでしょうか? よろしくお願いいたします。

  • splwtr
  • ベストアンサー率16% (75/461)
回答No.1

再帰やループで、関数を呼ぶ方法もありますけど CSV形式な数値データを、期待する数まで読み込みたいのですね。 事前にデータ数とか分からないのでしょうか? C++で、その様に表現するのは大変ですね。 事前に分かること、走査して判ることがあります。 この結果は反映してないのですか? こちらは、億単位でやってますが、一応、無事なルーチンです。 巨大なデータでも、繰り返し分割で読むこともできるし スタックする領域も拡張できるし 標準関数でなく、この辺は作るところ自分でだと思います。 答えは言いませんが、アルゴリズムの問題です。

tekcycle
質問者

お礼

読み込みではなくファイル出力なのですが。 それとも、プログラム中で計算結果を変数(か何か)に読み込ませるということでしょうか? .csvファイルの読み込みは、ネットを検索して良さそうなクラスを使わせていただきました。(ソースは一週間経ってもさっぱり理解できません。とにかく面倒だということは判りました。) これとバッチファイルを組み合わせれば、分割読み込みの必要があってももどうにかなるかなぁという感じです。(これはこれでまた問題があるので別に質問いたします。) データ数というのはどこのことを仰っているのか判りません。 出力行数のことならループの回数そのものです。 億のこともしたいのですが今のところ2~3百万で様子を見ようと思っております。 ループ数を制御できるように作ってあり、例えば今回は1~10000番まで、次回は10001回から38564番まで、等とできるようにしてあります。 1行当たりの、Excell的に言うなら列数も判っています。 データ数というのはこういう事でよろしいのでしょうか? やってみないと判りませんが、3000ループで3000行の結果が出る毎に書き込むようにするようなことを考えております。 自力で作れと言われれば2~3行ならともかくお手上げです。 標準関数の利用がギリギリという感じです。

関連するQ&A

専門家に質問してみよう