ifstreamにハマる

c++のファイルストリームにハマってしまいました.
簡略化すると,内容は要はファイルを読むプログラム.最初のプログラムは,こんな感じでした.

プログラムtmp.cc

#include // ifstream
#include // cout
using namespace std;
class File
{
  ifstream *ifst;
public:
  File(char *file);
  ~File();
  int each(char *line, int size);
};
File::File(char *file)
{
  ifst = new ifstream(file);
  if(!(*ifst))
  {
    cerr<<"file["<getline(line, size);
  return(ifst->eof());
}
int main()
{
  File fs("tmp.cc");
  char buf[256];
  while(!fs.each(buf,255)) cout<

自分自身を出力するだけのプログラムです.
ストラウストラップのc++本に,「一行を読む時はget()よりもgetline()を使うほうが良い場合がある」と書いてあったので,
http://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9EC-%E3%82%A2%E3%82%B9%E3%82%AD%E3%83%BC%E3%82%A2%E3%82%B8%E3%82%BD%E3%83%B3%E3%82%A6%E3%82%A7%E3%82%B9%E3%83%AC%E3%82%A4%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%E2%80%95Ascii-Addison-Wesley-programming/dp/475611895X
今までは自前でよくget()を使って一行を読むルーチンを書いていたのですが,getline()を使ってみました.
とりあえず,何の問題も無く動きました.

ただし,せっかく一行読み込みのバッファサイズを指定しているので,バッファが足りない場合の動作を検証したくて,次のようにmain()を変更して実行してみました.コンパイルには問題はありませんでした.

int main()
{
  File fs("tmp.cc");
  char buf[256];
  while(!fs.each(buf,25)) cout<

実行結果は,

#include // ifs





(以下,無限ループ)

最初の一行目の24文字(+終端文字1文字)が出力され,あとは無限ループです.
そこでデバグコードを入れて無限ループの間に何が出力されているか調べると,エラーコードの「-1」でした.
ストラウストラップ本には詳しくは書いていなかったのですが,読み込みバッファの指定数が読み込む行の文字数よりも少ない場合,エラービットが立つそうです.エラービットが立った場合,クリアすると無限ループは解消されました.

改良ルーチン

int File::each(char *line, int size)
{
  ifst->getline(line, size);
  if(ifst->gcount()==(size-1))
  {
     ifst->clear();
  }
  return(ifst->eof());
}


実行結果

#include // ifs
tream
#include // co
ut
using namespace std;
class File
{
  ifstream *ifst;

これはこれで正しい気がするのですが,バッファが足りなかったら,次のループで読むのではなくて,あきらめて切り捨てた方が良い場合もある気がしました.そこでさらに改造.

int File::each(char *line, int size)
{
  ifst->getline(line, size);
  if(ifst->gcount()==(size-1))
  {
     ifst->clear();
     while(ifst->get() != '\n');
  }
  return(ifst->eof());
}

読み込めなかった文字を,改行まで読み飛ばしました.


実行結果

#include // ifs
#include // co
using namespace std;
class File
{
  ifstream *ifst;

本当はバッファは256文字分(終端文字含む)あるのですが,それを25文字(終端文字含む)と指定することで,24文字まで読み込んで出力することができました.