物理の研究の備忘録

高エネルギー物理学とかいうマニアックな研究分野の博士課程にいるわたしの備忘録。主にPCの設定とか

c++ でテキストファイルを読み込んでループを回す時のメモ

データ解析においてテキストファイルに書かれたデータを読み込む機会は結構多い。

俺も今までさんざん使ってきたんだけど、結構細かい書き方忘れちゃうし、曖昧な箇所も多いのでこの際ちゃんと覚える。

例えば次のような内容のテキストファイル(test.dat)があったとする。

1  2

3  4

これを読み込んでみよう。

 

書き方その1

 void test(){
    int a,b;
    ifstream ifs;
    ifs.open("test.dat");
    int loop=0;
    ifs>>a>>b;
    while(!ifs.eof()){

    cout<<"loop="<<loop<<endl;
    cout<<"a,b="<<a<<","<<b<<endl;
    ifs>>a>>b;
    loop++;
  }

}

 

出力結果

loop=0

a,b=1,2

loop=1

a,b=3,4

メモ

ヘッダはfstream.hが必要。using name spaceとかは書いてないけどコンパイルするならほんとは必要。ファイルの読み込みはfstreamを通して行う。

ファイルを開くとき、ここでは一度fstreamクラスのオブジェクト(ifs)をつくってから、openという関数を使っているが、ifstream("test.dat")と一気にコンストラクタで開くことも可能。まぁどっちでもいい。

loopはloopの回数数えてるだけので別になくてもいい。ただ、for分と違って無限loopが怖いのでwhile文の場合はloop回数についつい神経質になってしまう。

eof()は読みこむ際に最後の行であるかを判定してtrue(0)かfalse(1)を返す。

うまく読み込めればfalse、最後までいって読み込めなくなったらtrueを返す。

  while文はtrueの時だけ回るので!がついているというわけ。

コードをよくよく見ると、1行目をloopの外で読み込んでからloopに突入し、表示→読み込みの順番でloopを繰り返しているが、これにはちゃんと意味がある。下の悪い例を見てほしい。

 

悪い書き方

 void test(){
    int a,b;
    ifstream ifs;
    ifs.open("test.dat");
    int loop=0;
    while(!ifs.eof()){

   ifs>>a>>b;

    cout<<"loop="<<loop<<endl;
    cout<<"a,b="<<a<<","<<b<<endl;
    loop++;
  }

}

 

出力結果

loop=0

a,b=1,2

loop=1

a,b=3,4

loop=2

a,b=3,4

 

違いはloopの中で読み込み→表示を繰り返してること。見てわかるように最後に一回余分にloopが回ってる。

この不具合は!eof()が最後の行を読み込んだときはtrueでその次の存在しない行を読み込んだ時に初めてfalseを出力するために発生する。

つまり、最後の存在しない行を読み込んで!eof()がfalseになった直後に表示を余分にしてしまうのだ。このへんはなんとなくコードを書いているとやってしまいがちなので注意する。(というかやってしまった)

書き方その2

 void test(){
    int a,b;
    ifstream ifs;
    ifs.open("test.dat");
    int loop=0;
    while(ifs>>a>>b){

    cout<<"loop="<<loop<<endl;
    cout<<"a,b="<<a<<","<<b<<endl;
    loop++;
  }

}

違いはwhile文の中身で、ifs>>a>>bの読み込みがうまくいってる限りloopが回り、読み込めなくなった時点で止まる。さっきよりすっきりしてるし、例の一回余分に読み込んでしまう不具合も発生しない。難点を挙げるとすれば読み込みのifs>>...の部分が長くなったときにwhile文のかっこの中身がごちゃごちゃしてしまって見づらいくらい?いやほんとに難点って言っていいのかわからないけど。

 

ちなみに言うまでもないけど、for文はloopの回数がわかってる時には使えるが、

datファイルの中身が何行あるかわからないときは今回みたいにwhile文を使うしかない

個人的にはfor文のほうが好きだけど致し方ない。

 

まとめ

・テキストファイルの読み込みはfstreamを使いwhile文でloopを回す

・fstream::eof()を使うときは最後の行の振る舞いに注意

 

おわり!